1ba371819SOllivier Robert /* 2ba371819SOllivier Robert * 39c2daa00SOllivier Robert * Refclock_neoclock4x.c 4ba371819SOllivier Robert * - NeoClock4X driver for DCF77 or FIA Timecode 5ba371819SOllivier Robert * 62b15cb3dSCy Schubert * Date: 2009-12-04 v1.16 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 */ 12ba371819SOllivier Robert 13ba371819SOllivier Robert #ifdef HAVE_CONFIG_H 14ba371819SOllivier Robert # include "config.h" 15ba371819SOllivier Robert #endif 16ba371819SOllivier Robert 17ba371819SOllivier Robert #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X)) 18ba371819SOllivier Robert 19ba371819SOllivier Robert #include <unistd.h> 20ba371819SOllivier Robert #include <sys/time.h> 21ba371819SOllivier Robert #include <sys/types.h> 22ba371819SOllivier Robert #include <termios.h> 23ba371819SOllivier Robert #include <sys/ioctl.h> 24ba371819SOllivier Robert #include <ctype.h> 25ba371819SOllivier Robert 26ba371819SOllivier Robert #include "ntpd.h" 27ba371819SOllivier Robert #include "ntp_io.h" 28ba371819SOllivier Robert #include "ntp_control.h" 29ba371819SOllivier Robert #include "ntp_refclock.h" 30ba371819SOllivier Robert #include "ntp_unixtime.h" 31ba371819SOllivier Robert #include "ntp_stdlib.h" 32ba371819SOllivier Robert 33ba371819SOllivier Robert #if defined HAVE_SYS_MODEM_H 34ba371819SOllivier Robert # include <sys/modem.h> 35ea906c41SOllivier Robert # ifndef __QNXNTO__ 36ba371819SOllivier Robert # define TIOCMSET MCSETA 37ba371819SOllivier Robert # define TIOCMGET MCGETA 38ba371819SOllivier Robert # define TIOCM_RTS MRTS 39ba371819SOllivier Robert # endif 40ea906c41SOllivier Robert #endif 41ba371819SOllivier Robert 42ba371819SOllivier Robert #ifdef HAVE_TERMIOS_H 43ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3 44ba371819SOllivier Robert # define _SVID3 45ba371819SOllivier Robert # endif 46ba371819SOllivier Robert # include <termios.h> 47ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3 48ba371819SOllivier Robert # undef _SVID3 49ba371819SOllivier Robert # endif 50ba371819SOllivier Robert #endif 51ba371819SOllivier Robert 52ba371819SOllivier Robert #ifdef HAVE_SYS_IOCTL_H 53ba371819SOllivier Robert # include <sys/ioctl.h> 54ba371819SOllivier Robert #endif 55ba371819SOllivier Robert 569c2daa00SOllivier Robert /* 57ea906c41SOllivier Robert * NTP version 4.20 change the pp->msec field to pp->nsec. 58ea906c41SOllivier Robert * To allow to support older ntp versions with this sourcefile 59ea906c41SOllivier Robert * you can define NTP_PRE_420 to allow this driver to compile 60ea906c41SOllivier Robert * with ntp version back to 4.1.2. 61ea906c41SOllivier Robert * 62ea906c41SOllivier Robert */ 63ea906c41SOllivier Robert #if 0 64ea906c41SOllivier Robert #define NTP_PRE_420 65ea906c41SOllivier Robert #endif 66ea906c41SOllivier Robert 67ea906c41SOllivier Robert /* 689c2daa00SOllivier Robert * If you want the driver for whatever reason to not use 699c2daa00SOllivier Robert * the TX line to send anything to your NeoClock4X 709c2daa00SOllivier Robert * device you must tell the NTP refclock driver which 719c2daa00SOllivier Robert * firmware you NeoClock4X device uses. 729c2daa00SOllivier Robert * 739c2daa00SOllivier Robert * If you want to enable this feature change the "#if 0" 749c2daa00SOllivier Robert * line to "#if 1" and make sure that the defined firmware 759c2daa00SOllivier Robert * matches the firmware off your NeoClock4X receiver! 769c2daa00SOllivier Robert * 779c2daa00SOllivier Robert */ 789c2daa00SOllivier Robert 799c2daa00SOllivier Robert #if 0 809c2daa00SOllivier Robert #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A 819c2daa00SOllivier Robert #endif 829c2daa00SOllivier Robert 83ea906c41SOllivier Robert /* at this time only firmware version A is known */ 849c2daa00SOllivier Robert #define NEOCLOCK4X_FIRMWARE_VERSION_A 'A' 859c2daa00SOllivier Robert 86ba371819SOllivier Robert #define NEOCLOCK4X_TIMECODELEN 37 87ba371819SOllivier Robert 88ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SERIAL 3 89ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9 90ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DAY 12 91ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MONTH 14 92ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_YEAR 16 93ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HOUR 18 94ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MINUTE 20 95ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SECOND 22 96ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HSEC 24 97ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DOW 26 98ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_TIMESOURCE 28 99ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DSTSTATUS 29 100ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30 101ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA1 31 102ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA2 33 103ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_CRC 35 104ba371819SOllivier Robert 1052b15cb3dSCy Schubert #define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)" 106ea906c41SOllivier Robert 107ea906c41SOllivier Robert #define NSEC_TO_MILLI 1000000 1089c2daa00SOllivier Robert 109ba371819SOllivier Robert struct neoclock4x_unit { 110ba371819SOllivier Robert l_fp laststamp; /* last receive timestamp */ 111ba371819SOllivier Robert short unit; /* NTP refclock unit number */ 112ba371819SOllivier Robert u_long polled; /* flag to detect noreplies */ 113ba371819SOllivier Robert char leap_status; /* leap second flag */ 114ba371819SOllivier Robert int recvnow; 115ba371819SOllivier Robert 116ba371819SOllivier Robert char firmware[80]; 1179c2daa00SOllivier Robert char firmwaretag; 118ba371819SOllivier Robert char serial[7]; 119ba371819SOllivier Robert char radiosignal[4]; 120ba371819SOllivier Robert char timesource; 121ba371819SOllivier Robert char dststatus; 122ba371819SOllivier Robert char quarzstatus; 123ba371819SOllivier Robert int antenna1; 124ba371819SOllivier Robert int antenna2; 125ba371819SOllivier Robert int utc_year; 126ba371819SOllivier Robert int utc_month; 127ba371819SOllivier Robert int utc_day; 128ba371819SOllivier Robert int utc_hour; 129ba371819SOllivier Robert int utc_minute; 130ba371819SOllivier Robert int utc_second; 131ba371819SOllivier Robert int utc_msec; 132ba371819SOllivier Robert }; 133ba371819SOllivier Robert 1342b15cb3dSCy Schubert static int neoclock4x_start (int, struct peer *); 1352b15cb3dSCy Schubert static void neoclock4x_shutdown (int, struct peer *); 1362b15cb3dSCy Schubert static void neoclock4x_receive (struct recvbuf *); 1372b15cb3dSCy Schubert static void neoclock4x_poll (int, struct peer *); 1382b15cb3dSCy Schubert static void neoclock4x_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); 139ba371819SOllivier Robert 1402b15cb3dSCy Schubert static int neol_atoi_len (const char str[], int *, int); 1412b15cb3dSCy Schubert static int neol_hexatoi_len (const char str[], int *, int); 1422b15cb3dSCy Schubert static void neol_jdn_to_ymd (unsigned long, int *, int *, int *); 1432b15cb3dSCy Schubert static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*); 1442b15cb3dSCy Schubert static unsigned long neol_mktime (int, int, int, int, int, int); 1459c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE) 1462b15cb3dSCy Schubert static int neol_query_firmware (int, int, char *, size_t); 1472b15cb3dSCy Schubert static int neol_check_firmware (int, const char*, char *); 1489c2daa00SOllivier Robert #endif 149ba371819SOllivier Robert 150ba371819SOllivier Robert struct refclock refclock_neoclock4x = { 151ba371819SOllivier Robert neoclock4x_start, /* start up driver */ 152ba371819SOllivier Robert neoclock4x_shutdown, /* shut down driver */ 153ba371819SOllivier Robert neoclock4x_poll, /* transmit poll message */ 154ba371819SOllivier Robert neoclock4x_control, 155ba371819SOllivier Robert noentry, /* initialize driver (not used) */ 156ba371819SOllivier Robert noentry, /* not used */ 157ba371819SOllivier Robert NOFLAGS /* not used */ 158ba371819SOllivier Robert }; 159ba371819SOllivier Robert 160ba371819SOllivier Robert static int 161ba371819SOllivier Robert neoclock4x_start(int unit, 162ba371819SOllivier Robert struct peer *peer) 163ba371819SOllivier Robert { 164ba371819SOllivier Robert struct neoclock4x_unit *up; 165ba371819SOllivier Robert struct refclockproc *pp; 166ba371819SOllivier Robert int fd; 167ba371819SOllivier Robert char dev[20]; 168ba371819SOllivier Robert int sl232; 1699c2daa00SOllivier Robert #if defined(HAVE_TERMIOS) 170ba371819SOllivier Robert struct termios termsettings; 1719c2daa00SOllivier Robert #endif 1729c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE) 173ba371819SOllivier Robert int tries; 1749c2daa00SOllivier Robert #endif 175ba371819SOllivier Robert 1769c2daa00SOllivier Robert (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); 177ba371819SOllivier Robert 178ba371819SOllivier Robert /* LDISC_STD, LDISC_RAW 179ba371819SOllivier Robert * Open serial port. Use CLK line discipline, if available. 180ba371819SOllivier Robert */ 181a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, dev, B2400, LDISC_STD); 182ba371819SOllivier Robert if(fd <= 0) 183ba371819SOllivier Robert { 184ba371819SOllivier Robert return (0); 185ba371819SOllivier Robert } 186ba371819SOllivier Robert 1879c2daa00SOllivier Robert #if defined(HAVE_TERMIOS) 188ea906c41SOllivier Robert 189ea906c41SOllivier Robert #if 1 190ea906c41SOllivier Robert if(tcgetattr(fd, &termsettings) < 0) 191ea906c41SOllivier Robert { 192ea906c41SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 193ea906c41SOllivier Robert (void) close(fd); 194ea906c41SOllivier Robert return (0); 195ea906c41SOllivier Robert } 196ea906c41SOllivier Robert 197ea906c41SOllivier Robert /* 2400 Baud 8N2 */ 198ea906c41SOllivier Robert termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL; 199ea906c41SOllivier Robert termsettings.c_oflag = 0; 200ea906c41SOllivier Robert termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD; 201ea906c41SOllivier Robert (void)cfsetispeed(&termsettings, (u_int)B2400); 202ea906c41SOllivier Robert (void)cfsetospeed(&termsettings, (u_int)B2400); 203ea906c41SOllivier Robert 204ea906c41SOllivier Robert if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 205ea906c41SOllivier Robert { 206ea906c41SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 207ea906c41SOllivier Robert (void) close(fd); 208ea906c41SOllivier Robert return (0); 209ea906c41SOllivier Robert } 210ea906c41SOllivier Robert 211ea906c41SOllivier Robert #else 2129c2daa00SOllivier Robert if(tcgetattr(fd, &termsettings) < 0) 2139c2daa00SOllivier Robert { 2149c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 2159c2daa00SOllivier Robert (void) close(fd); 2169c2daa00SOllivier Robert return (0); 2179c2daa00SOllivier Robert } 2189c2daa00SOllivier Robert 2199c2daa00SOllivier Robert /* 2400 Baud 8N2 */ 2209c2daa00SOllivier Robert termsettings.c_cflag &= ~PARENB; 2219c2daa00SOllivier Robert termsettings.c_cflag |= CSTOPB; 2229c2daa00SOllivier Robert termsettings.c_cflag &= ~CSIZE; 2239c2daa00SOllivier Robert termsettings.c_cflag |= CS8; 2249c2daa00SOllivier Robert 2259c2daa00SOllivier Robert if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 2269c2daa00SOllivier Robert { 2279c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 2289c2daa00SOllivier Robert (void) close(fd); 2299c2daa00SOllivier Robert return (0); 2309c2daa00SOllivier Robert } 231ea906c41SOllivier Robert #endif 232ea906c41SOllivier Robert 2339c2daa00SOllivier Robert #elif defined(HAVE_SYSV_TTYS) 2349c2daa00SOllivier Robert if(ioctl(fd, TCGETA, &termsettings) < 0) 2359c2daa00SOllivier Robert { 2369c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit); 2379c2daa00SOllivier Robert (void) close(fd); 2389c2daa00SOllivier Robert return (0); 2399c2daa00SOllivier Robert } 2409c2daa00SOllivier Robert 2419c2daa00SOllivier Robert /* 2400 Baud 8N2 */ 2429c2daa00SOllivier Robert termsettings.c_cflag &= ~PARENB; 2439c2daa00SOllivier Robert termsettings.c_cflag |= CSTOPB; 2449c2daa00SOllivier Robert termsettings.c_cflag &= ~CSIZE; 2459c2daa00SOllivier Robert termsettings.c_cflag |= CS8; 2469c2daa00SOllivier Robert 2479c2daa00SOllivier Robert if(ioctl(fd, TCSETA, &termsettings) < 0) 2489c2daa00SOllivier Robert { 2499c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit); 2509c2daa00SOllivier Robert (void) close(fd); 2519c2daa00SOllivier Robert return (0); 2529c2daa00SOllivier Robert } 2539c2daa00SOllivier Robert #else 2549c2daa00SOllivier Robert msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit); 2559c2daa00SOllivier Robert (void) close(fd); 2569c2daa00SOllivier Robert return (0); 2579c2daa00SOllivier Robert #endif 2589c2daa00SOllivier Robert 259ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 260ba371819SOllivier Robert /* turn on RTS, and DTR for power supply */ 261ba371819SOllivier Robert /* NeoClock4x is powered from serial line */ 262ba371819SOllivier Robert if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1) 263ba371819SOllivier Robert { 264ba371819SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); 2659c2daa00SOllivier Robert (void) close(fd); 2669c2daa00SOllivier Robert return (0); 267ba371819SOllivier Robert } 268ba371819SOllivier Robert #ifdef TIOCM_RTS 269ba371819SOllivier Robert sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */ 270ba371819SOllivier Robert #else 271ba371819SOllivier Robert sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */ 272ba371819SOllivier Robert #endif 273ba371819SOllivier Robert if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1) 274ba371819SOllivier Robert { 275ba371819SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); 2769c2daa00SOllivier Robert (void) close(fd); 2779c2daa00SOllivier Robert return (0); 278ba371819SOllivier Robert } 279ba371819SOllivier Robert #else 2809c2daa00SOllivier Robert msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!", 281ba371819SOllivier Robert unit); 2829c2daa00SOllivier Robert (void) close(fd); 2839c2daa00SOllivier Robert return (0); 284ba371819SOllivier Robert #endif 285ba371819SOllivier Robert 286ba371819SOllivier Robert up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); 287ba371819SOllivier Robert if(!(up)) 288ba371819SOllivier Robert { 289ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); 290ba371819SOllivier Robert (void) close(fd); 291ba371819SOllivier Robert return (0); 292ba371819SOllivier Robert } 293ba371819SOllivier Robert 294ba371819SOllivier Robert memset((char *)up, 0, sizeof(struct neoclock4x_unit)); 295ba371819SOllivier Robert pp = peer->procptr; 296ba371819SOllivier Robert pp->clockdesc = "NeoClock4X"; 2972b15cb3dSCy Schubert pp->unitptr = up; 298ba371819SOllivier Robert pp->io.clock_recv = neoclock4x_receive; 2992b15cb3dSCy Schubert pp->io.srcclock = peer; 300ba371819SOllivier Robert pp->io.datalen = 0; 301ba371819SOllivier Robert pp->io.fd = fd; 3029c2daa00SOllivier Robert /* 3039c2daa00SOllivier Robert * no fudge time is given by user! 3049c2daa00SOllivier Robert * use 169.583333 ms to compensate the serial line delay 305ba371819SOllivier Robert * formula is: 306ba371819SOllivier Robert * 2400 Baud / 11 bit = 218.18 charaters per second 307ba371819SOllivier Robert * (NeoClock4X timecode len) 308ba371819SOllivier Robert */ 309ba371819SOllivier Robert pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; 310ba371819SOllivier Robert 311ba371819SOllivier Robert /* 312ba371819SOllivier Robert * Initialize miscellaneous variables 313ba371819SOllivier Robert */ 314ba371819SOllivier Robert peer->precision = -10; 315ba371819SOllivier Robert memcpy((char *)&pp->refid, "neol", 4); 316ba371819SOllivier Robert 317ba371819SOllivier Robert up->leap_status = 0; 318ba371819SOllivier Robert up->unit = unit; 3192b15cb3dSCy Schubert strlcpy(up->firmware, "?", sizeof(up->firmware)); 3209c2daa00SOllivier Robert up->firmwaretag = '?'; 3212b15cb3dSCy Schubert strlcpy(up->serial, "?", sizeof(up->serial)); 3222b15cb3dSCy Schubert strlcpy(up->radiosignal, "?", sizeof(up->radiosignal)); 323ba371819SOllivier Robert up->timesource = '?'; 324ba371819SOllivier Robert up->dststatus = '?'; 325ba371819SOllivier Robert up->quarzstatus = '?'; 326ba371819SOllivier Robert up->antenna1 = -1; 327ba371819SOllivier Robert up->antenna2 = -1; 328ba371819SOllivier Robert up->utc_year = 0; 329ba371819SOllivier Robert up->utc_month = 0; 330ba371819SOllivier Robert up->utc_day = 0; 331ba371819SOllivier Robert up->utc_hour = 0; 332ba371819SOllivier Robert up->utc_minute = 0; 333ba371819SOllivier Robert up->utc_second = 0; 334ba371819SOllivier Robert up->utc_msec = 0; 335ba371819SOllivier Robert 3369c2daa00SOllivier Robert #if defined(NEOCLOCK4X_FIRMWARE) 3379c2daa00SOllivier Robert #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A 3382b15cb3dSCy Schubert strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)", 3392b15cb3dSCy Schubert sizeof(up->firmware)); 3409c2daa00SOllivier Robert up->firmwaretag = 'A'; 3419c2daa00SOllivier Robert #else 342ea906c41SOllivier Robert msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X", 3439c2daa00SOllivier Robert unit); 3449c2daa00SOllivier Robert (void) close(fd); 3459c2daa00SOllivier Robert pp->io.fd = -1; 3469c2daa00SOllivier Robert free(pp->unitptr); 3479c2daa00SOllivier Robert pp->unitptr = NULL; 3489c2daa00SOllivier Robert return (0); 3499c2daa00SOllivier Robert #endif 3509c2daa00SOllivier Robert #else 351ba371819SOllivier Robert for(tries=0; tries < 5; tries++) 352ba371819SOllivier Robert { 353ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 3549c2daa00SOllivier Robert msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries); 3559c2daa00SOllivier Robert /* wait 3 seconds for receiver to power up */ 356ba371819SOllivier Robert sleep(3); 357ba371819SOllivier Robert if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) 358ba371819SOllivier Robert { 359ba371819SOllivier Robert break; 360ba371819SOllivier Robert } 361ba371819SOllivier Robert } 362ba371819SOllivier Robert 3639c2daa00SOllivier Robert /* can I handle this firmware version? */ 3649c2daa00SOllivier Robert if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) 3659c2daa00SOllivier Robert { 3669c2daa00SOllivier Robert (void) close(fd); 3679c2daa00SOllivier Robert pp->io.fd = -1; 3689c2daa00SOllivier Robert free(pp->unitptr); 3699c2daa00SOllivier Robert pp->unitptr = NULL; 3709c2daa00SOllivier Robert return (0); 3719c2daa00SOllivier Robert } 3729c2daa00SOllivier Robert #endif 3739c2daa00SOllivier Robert 3749c2daa00SOllivier Robert if(!io_addclock(&pp->io)) 3759c2daa00SOllivier Robert { 3769c2daa00SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit); 3779c2daa00SOllivier Robert (void) close(fd); 3789c2daa00SOllivier Robert pp->io.fd = -1; 3799c2daa00SOllivier Robert free(pp->unitptr); 3809c2daa00SOllivier Robert pp->unitptr = NULL; 3819c2daa00SOllivier Robert return (0); 3829c2daa00SOllivier Robert } 3839c2daa00SOllivier Robert 384ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 385ba371819SOllivier Robert msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); 386ba371819SOllivier Robert 387ba371819SOllivier Robert return (1); 388ba371819SOllivier Robert } 389ba371819SOllivier Robert 390ba371819SOllivier Robert static void 391ba371819SOllivier Robert neoclock4x_shutdown(int unit, 392ba371819SOllivier Robert struct peer *peer) 393ba371819SOllivier Robert { 394ba371819SOllivier Robert struct neoclock4x_unit *up; 395ba371819SOllivier Robert struct refclockproc *pp; 396ba371819SOllivier Robert int sl232; 397ba371819SOllivier Robert 3989c2daa00SOllivier Robert if(NULL != peer) 3999c2daa00SOllivier Robert { 400ba371819SOllivier Robert pp = peer->procptr; 4019c2daa00SOllivier Robert if(pp != NULL) 4029c2daa00SOllivier Robert { 4032b15cb3dSCy Schubert up = pp->unitptr; 4049c2daa00SOllivier Robert if(up != NULL) 4059c2daa00SOllivier Robert { 4069c2daa00SOllivier Robert if(-1 != pp->io.fd) 4079c2daa00SOllivier Robert { 408ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 409ba371819SOllivier Robert /* turn on RTS, and DTR for power supply */ 410ba371819SOllivier Robert /* NeoClock4x is powered from serial line */ 411ba371819SOllivier Robert if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1) 412ba371819SOllivier Robert { 4139c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", 4149c2daa00SOllivier Robert unit); 415ba371819SOllivier Robert } 416ba371819SOllivier Robert #ifdef TIOCM_RTS 4179c2daa00SOllivier Robert /* turn on RTS, and DTR for power supply */ 4189c2daa00SOllivier Robert sl232 &= ~(TIOCM_DTR | TIOCM_RTS); 419ba371819SOllivier Robert #else 4209c2daa00SOllivier Robert /* turn on RTS, and DTR for power supply */ 4219c2daa00SOllivier Robert sl232 &= ~(CIOCM_DTR | CIOCM_RTS); 422ba371819SOllivier Robert #endif 423ba371819SOllivier Robert if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) 424ba371819SOllivier Robert { 4259c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", 4269c2daa00SOllivier Robert unit); 427ba371819SOllivier Robert } 428ba371819SOllivier Robert #endif 4299c2daa00SOllivier Robert io_closeclock(&pp->io); 4309c2daa00SOllivier Robert } 4319c2daa00SOllivier Robert free(up); 4329c2daa00SOllivier Robert pp->unitptr = NULL; 4339c2daa00SOllivier Robert } 4349c2daa00SOllivier Robert } 4359c2daa00SOllivier Robert } 4369c2daa00SOllivier Robert 437ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); 438ba371819SOllivier Robert 439ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 440ba371819SOllivier Robert msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); 441ba371819SOllivier Robert } 442ba371819SOllivier Robert 443ba371819SOllivier Robert static void 444ba371819SOllivier Robert neoclock4x_receive(struct recvbuf *rbufp) 445ba371819SOllivier Robert { 446ba371819SOllivier Robert struct neoclock4x_unit *up; 447ba371819SOllivier Robert struct refclockproc *pp; 448ba371819SOllivier Robert struct peer *peer; 449ba371819SOllivier Robert unsigned long calc_utc; 450ba371819SOllivier Robert int day; 451ba371819SOllivier Robert int month; /* ddd conversion */ 452ba371819SOllivier Robert int c; 4539c2daa00SOllivier Robert int dsec; 454ba371819SOllivier Robert unsigned char calc_chksum; 455ba371819SOllivier Robert int recv_chksum; 456ba371819SOllivier Robert 4572b15cb3dSCy Schubert peer = rbufp->recv_peer; 458ba371819SOllivier Robert pp = peer->procptr; 4592b15cb3dSCy Schubert up = pp->unitptr; 460ba371819SOllivier Robert 461ba371819SOllivier Robert /* wait till poll interval is reached */ 462ba371819SOllivier Robert if(0 == up->recvnow) 463ba371819SOllivier Robert return; 464ba371819SOllivier Robert 465ba371819SOllivier Robert /* reset poll interval flag */ 466ba371819SOllivier Robert up->recvnow = 0; 467ba371819SOllivier Robert 468ba371819SOllivier Robert /* read last received timecode */ 469ba371819SOllivier Robert pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 4709c2daa00SOllivier Robert pp->leap = LEAP_NOWARNING; 471ba371819SOllivier Robert 472ba371819SOllivier Robert if(NEOCLOCK4X_TIMECODELEN != pp->lencode) 473ba371819SOllivier Robert { 474ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 475ba371819SOllivier Robert msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", 476ba371819SOllivier Robert up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); 477ba371819SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 478ba371819SOllivier Robert return; 479ba371819SOllivier Robert } 480ba371819SOllivier Robert 481ba371819SOllivier Robert neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); 482ba371819SOllivier Robert 483ba371819SOllivier Robert /* calculate checksum */ 484ba371819SOllivier Robert calc_chksum = 0; 485ba371819SOllivier Robert for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) 486ba371819SOllivier Robert { 487ba371819SOllivier Robert calc_chksum += pp->a_lastcode[c]; 488ba371819SOllivier Robert } 489ba371819SOllivier Robert if(recv_chksum != calc_chksum) 490ba371819SOllivier Robert { 491ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 492ba371819SOllivier Robert msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", 493ba371819SOllivier Robert up->unit, pp->a_lastcode); 494ba371819SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 495ba371819SOllivier Robert return; 496ba371819SOllivier Robert } 497ba371819SOllivier Robert 498ba371819SOllivier Robert /* Allow synchronization even is quartz clock is 499ba371819SOllivier Robert * never initialized. 500ba371819SOllivier Robert * WARNING: This is dangerous! 501ba371819SOllivier Robert */ 502ba371819SOllivier Robert up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; 503ba371819SOllivier Robert if(0==(pp->sloppyclockflag & CLK_FLAG2)) 504ba371819SOllivier Robert { 505ba371819SOllivier Robert if('I' != up->quarzstatus) 506ba371819SOllivier Robert { 507ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 508ba371819SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", 509ba371819SOllivier Robert up->unit, pp->a_lastcode); 510ba371819SOllivier Robert pp->leap = LEAP_NOTINSYNC; 511ba371819SOllivier Robert refclock_report(peer, CEVNT_BADDATE); 512ba371819SOllivier Robert return; 513ba371819SOllivier Robert } 514ba371819SOllivier Robert } 515ba371819SOllivier Robert if('I' != up->quarzstatus) 516ba371819SOllivier Robert { 517ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 518ba371819SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", 519ba371819SOllivier Robert up->unit, pp->a_lastcode); 520ba371819SOllivier Robert } 521ba371819SOllivier Robert 522ba371819SOllivier Robert /* 523ba371819SOllivier Robert * If NeoClock4X is not synchronized to a radio clock 524ba371819SOllivier Robert * check if we're allowed to synchronize with the quartz 525ba371819SOllivier Robert * clock. 526ba371819SOllivier Robert */ 527ba371819SOllivier Robert up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; 528ba371819SOllivier Robert if(0==(pp->sloppyclockflag & CLK_FLAG2)) 529ba371819SOllivier Robert { 530ba371819SOllivier Robert if('A' != up->timesource) 531ba371819SOllivier Robert { 532ba371819SOllivier Robert /* not allowed to sync with quartz clock */ 533ba371819SOllivier Robert if(0==(pp->sloppyclockflag & CLK_FLAG1)) 534ba371819SOllivier Robert { 535ba371819SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 536ba371819SOllivier Robert pp->leap = LEAP_NOTINSYNC; 537ba371819SOllivier Robert return; 538ba371819SOllivier Robert } 539ba371819SOllivier Robert } 540ba371819SOllivier Robert } 541ba371819SOllivier Robert 542ba371819SOllivier Robert /* this should only used when first install is done */ 543ba371819SOllivier Robert if(pp->sloppyclockflag & CLK_FLAG4) 544ba371819SOllivier Robert { 545ba371819SOllivier Robert msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", 546ba371819SOllivier Robert up->unit, pp->a_lastcode); 547ba371819SOllivier Robert } 548ba371819SOllivier Robert 549ba371819SOllivier Robert /* 123456789012345678901234567890123456789012345 */ 550ba371819SOllivier Robert /* S/N123456DCF1004021010001202ASX1213CR\r\n */ 551ba371819SOllivier Robert 552ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); 553ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); 554ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); 555ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); 556ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); 557ba371819SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); 5589c2daa00SOllivier Robert neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2); 559ea906c41SOllivier Robert #if defined(NTP_PRE_420) 560ea906c41SOllivier Robert pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */ 561ea906c41SOllivier Robert #else 562ea906c41SOllivier Robert pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */ 563ea906c41SOllivier Robert #endif 564ba371819SOllivier Robert 565ba371819SOllivier Robert memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); 566ba371819SOllivier Robert up->radiosignal[3] = 0; 567ba371819SOllivier Robert memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); 568ba371819SOllivier Robert up->serial[6] = 0; 569ba371819SOllivier Robert up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; 570ba371819SOllivier Robert neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); 571ba371819SOllivier Robert neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); 572ba371819SOllivier Robert 573ba371819SOllivier Robert /* 574ba371819SOllivier Robert Validate received values at least enough to prevent internal 575ba371819SOllivier Robert array-bounds problems, etc. 576ba371819SOllivier Robert */ 577ba371819SOllivier Robert if((pp->hour < 0) || (pp->hour > 23) || 578ba371819SOllivier Robert (pp->minute < 0) || (pp->minute > 59) || 579ba371819SOllivier Robert (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 580ba371819SOllivier Robert (day < 1) || (day > 31) || 581ba371819SOllivier Robert (month < 1) || (month > 12) || 582ba371819SOllivier Robert (pp->year < 0) || (pp->year > 99)) { 583ba371819SOllivier Robert /* Data out of range. */ 584ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 585ba371819SOllivier Robert msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", 586ba371819SOllivier Robert up->unit, pp->a_lastcode); 587ba371819SOllivier Robert refclock_report(peer, CEVNT_BADDATE); 588ba371819SOllivier Robert return; 589ba371819SOllivier Robert } 590ba371819SOllivier Robert 5919c2daa00SOllivier Robert /* Year-2000 check not needed anymore. Same problem 5929c2daa00SOllivier Robert * will arise at 2099 but what should we do...? 5939c2daa00SOllivier Robert * 5949c2daa00SOllivier Robert * wrap 2-digit date into 4-digit 5959c2daa00SOllivier Robert * 5969c2daa00SOllivier Robert * if(pp->year < YEAR_PIVOT) 5979c2daa00SOllivier Robert * { 5989c2daa00SOllivier Robert * pp->year += 100; 5999c2daa00SOllivier Robert * } 6009c2daa00SOllivier Robert */ 6019c2daa00SOllivier Robert pp->year += 2000; 602ba371819SOllivier Robert 6039c2daa00SOllivier Robert /* adjust NeoClock4X local time to UTC */ 604ba371819SOllivier Robert calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); 605ba371819SOllivier Robert calc_utc -= 3600; 6069c2daa00SOllivier Robert /* adjust NeoClock4X daylight saving time if needed */ 607ba371819SOllivier Robert if('S' == up->dststatus) 608ba371819SOllivier Robert calc_utc -= 3600; 609ba371819SOllivier Robert neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); 610ba371819SOllivier Robert 611ba371819SOllivier Robert /* 612ba371819SOllivier Robert some preparations 613ba371819SOllivier Robert */ 614ba371819SOllivier Robert pp->day = ymd2yd(pp->year, month, day); 615ba371819SOllivier Robert pp->leap = 0; 616ba371819SOllivier Robert 617ba371819SOllivier Robert if(pp->sloppyclockflag & CLK_FLAG4) 618ba371819SOllivier Robert { 6199c2daa00SOllivier Robert msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld", 620ba371819SOllivier Robert up->unit, 621ba371819SOllivier Robert pp->year, month, day, 622ea906c41SOllivier Robert pp->hour, pp->minute, pp->second, 623ea906c41SOllivier Robert #if defined(NTP_PRE_420) 624ea906c41SOllivier Robert pp->msec 625ea906c41SOllivier Robert #else 626ea906c41SOllivier Robert pp->nsec/NSEC_TO_MILLI 627ea906c41SOllivier Robert #endif 628ea906c41SOllivier Robert ); 629ba371819SOllivier Robert } 630ba371819SOllivier Robert 631ba371819SOllivier Robert up->utc_year = pp->year; 632ba371819SOllivier Robert up->utc_month = month; 633ba371819SOllivier Robert up->utc_day = day; 634ba371819SOllivier Robert up->utc_hour = pp->hour; 635ba371819SOllivier Robert up->utc_minute = pp->minute; 636ba371819SOllivier Robert up->utc_second = pp->second; 637ea906c41SOllivier Robert #if defined(NTP_PRE_420) 638ea906c41SOllivier Robert up->utc_msec = pp->msec; 639ea906c41SOllivier Robert #else 640ea906c41SOllivier Robert up->utc_msec = pp->nsec/NSEC_TO_MILLI; 641ea906c41SOllivier Robert #endif 642ba371819SOllivier Robert 643ba371819SOllivier Robert if(!refclock_process(pp)) 644ba371819SOllivier Robert { 645ba371819SOllivier Robert NLOG(NLOG_CLOCKEVENT) 646ba371819SOllivier Robert msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); 647ba371819SOllivier Robert refclock_report(peer, CEVNT_FAULT); 648ba371819SOllivier Robert return; 649ba371819SOllivier Robert } 650ba371819SOllivier Robert refclock_receive(peer); 651ba371819SOllivier Robert 6529c2daa00SOllivier Robert /* report good status */ 6539c2daa00SOllivier Robert refclock_report(peer, CEVNT_NOMINAL); 6549c2daa00SOllivier Robert 655ba371819SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 656ba371819SOllivier Robert } 657ba371819SOllivier Robert 658ba371819SOllivier Robert static void 659ba371819SOllivier Robert neoclock4x_poll(int unit, 660ba371819SOllivier Robert struct peer *peer) 661ba371819SOllivier Robert { 662ba371819SOllivier Robert struct neoclock4x_unit *up; 663ba371819SOllivier Robert struct refclockproc *pp; 664ba371819SOllivier Robert 665ba371819SOllivier Robert pp = peer->procptr; 6662b15cb3dSCy Schubert up = pp->unitptr; 667ba371819SOllivier Robert 668ba371819SOllivier Robert pp->polls++; 669ba371819SOllivier Robert up->recvnow = 1; 670ba371819SOllivier Robert } 671ba371819SOllivier Robert 672ba371819SOllivier Robert static void 673ba371819SOllivier Robert neoclock4x_control(int unit, 6742b15cb3dSCy Schubert const struct refclockstat *in, 675ba371819SOllivier Robert struct refclockstat *out, 676ba371819SOllivier Robert struct peer *peer) 677ba371819SOllivier Robert { 678ba371819SOllivier Robert struct neoclock4x_unit *up; 679ba371819SOllivier Robert struct refclockproc *pp; 680ba371819SOllivier Robert 681ba371819SOllivier Robert if(NULL == peer) 682ba371819SOllivier Robert { 683ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 684ba371819SOllivier Robert return; 685ba371819SOllivier Robert } 686ba371819SOllivier Robert 687ba371819SOllivier Robert pp = peer->procptr; 688ba371819SOllivier Robert if(NULL == pp) 689ba371819SOllivier Robert { 690ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 691ba371819SOllivier Robert return; 692ba371819SOllivier Robert } 693ba371819SOllivier Robert 6942b15cb3dSCy Schubert up = pp->unitptr; 695ba371819SOllivier Robert if(NULL == up) 696ba371819SOllivier Robert { 697ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 698ba371819SOllivier Robert return; 699ba371819SOllivier Robert } 700ba371819SOllivier Robert 701ba371819SOllivier Robert if(NULL != in) 702ba371819SOllivier Robert { 703ba371819SOllivier Robert /* check to see if a user supplied time offset is given */ 704ba371819SOllivier Robert if(in->haveflags & CLK_HAVETIME1) 705ba371819SOllivier Robert { 706ba371819SOllivier Robert pp->fudgetime1 = in->fudgetime1; 707ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 708ba371819SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", 709ba371819SOllivier Robert unit, pp->fudgetime1); 710ba371819SOllivier Robert } 711ba371819SOllivier Robert 712ba371819SOllivier Robert /* notify */ 713ba371819SOllivier Robert if(pp->sloppyclockflag & CLK_FLAG1) 714ba371819SOllivier Robert { 715ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 716ba371819SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); 717ba371819SOllivier Robert } 718ba371819SOllivier Robert else 719ba371819SOllivier Robert { 720ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 721ba371819SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); 722ba371819SOllivier Robert } 723ba371819SOllivier Robert } 724ba371819SOllivier Robert 725ba371819SOllivier Robert if(NULL != out) 726ba371819SOllivier Robert { 727ba371819SOllivier Robert char *tt; 728ba371819SOllivier Robert char tmpbuf[80]; 729ba371819SOllivier Robert 730ba371819SOllivier Robert out->kv_list = (struct ctl_var *)0; 731ba371819SOllivier Robert out->type = REFCLK_NEOCLOCK4X; 732ba371819SOllivier Robert 7339c2daa00SOllivier Robert snprintf(tmpbuf, sizeof(tmpbuf)-1, 7349c2daa00SOllivier Robert "%04d-%02d-%02d %02d:%02d:%02d.%03d", 735ba371819SOllivier Robert up->utc_year, up->utc_month, up->utc_day, 736ba371819SOllivier Robert up->utc_hour, up->utc_minute, up->utc_second, 737ba371819SOllivier Robert up->utc_msec); 7389c2daa00SOllivier Robert tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF); 7399c2daa00SOllivier Robert snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf); 740ba371819SOllivier Robert 7419c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 7429c2daa00SOllivier Robert snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal); 7439c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 7449c2daa00SOllivier Robert snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1); 7459c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 7469c2daa00SOllivier Robert snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2); 7479c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 748ba371819SOllivier Robert if('A' == up->timesource) 7499c2daa00SOllivier Robert snprintf(tt, 39, "timesource=\"radio\""); 750ba371819SOllivier Robert else if('C' == up->timesource) 7519c2daa00SOllivier Robert snprintf(tt, 39, "timesource=\"quartz\""); 752ba371819SOllivier Robert else 7539c2daa00SOllivier Robert snprintf(tt, 39, "timesource=\"unknown\""); 7549c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 755ba371819SOllivier Robert if('I' == up->quarzstatus) 7569c2daa00SOllivier Robert snprintf(tt, 39, "quartzstatus=\"synchronized\""); 757ba371819SOllivier Robert else if('X' == up->quarzstatus) 7589c2daa00SOllivier Robert snprintf(tt, 39, "quartzstatus=\"not synchronized\""); 759ba371819SOllivier Robert else 7609c2daa00SOllivier Robert snprintf(tt, 39, "quartzstatus=\"unknown\""); 7619c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 762ba371819SOllivier Robert if('S' == up->dststatus) 7639c2daa00SOllivier Robert snprintf(tt, 39, "dststatus=\"summer\""); 764ba371819SOllivier Robert else if('W' == up->dststatus) 7659c2daa00SOllivier Robert snprintf(tt, 39, "dststatus=\"winter\""); 766ba371819SOllivier Robert else 7679c2daa00SOllivier Robert snprintf(tt, 39, "dststatus=\"unknown\""); 7689c2daa00SOllivier Robert tt = add_var(&out->kv_list, 80, RO|DEF); 7699c2daa00SOllivier Robert snprintf(tt, 79, "firmware=\"%s\"", up->firmware); 7709c2daa00SOllivier Robert tt = add_var(&out->kv_list, 40, RO|DEF); 7719c2daa00SOllivier Robert snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag); 7729c2daa00SOllivier Robert tt = add_var(&out->kv_list, 80, RO|DEF); 7739c2daa00SOllivier Robert snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION); 7749c2daa00SOllivier Robert tt = add_var(&out->kv_list, 80, RO|DEF); 7759c2daa00SOllivier Robert snprintf(tt, 79, "serialnumber=\"%s\"", up->serial); 776ba371819SOllivier Robert } 777ba371819SOllivier Robert } 778ba371819SOllivier Robert 7799c2daa00SOllivier Robert static int 7809c2daa00SOllivier Robert neol_hexatoi_len(const char str[], 781ba371819SOllivier Robert int *result, 782ba371819SOllivier Robert int maxlen) 783ba371819SOllivier Robert { 784ba371819SOllivier Robert int hexdigit; 785ba371819SOllivier Robert int i; 786ba371819SOllivier Robert int n = 0; 787ba371819SOllivier Robert 7882b15cb3dSCy Schubert for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++) 789ba371819SOllivier Robert { 7902b15cb3dSCy Schubert hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10; 791ba371819SOllivier Robert n = 16 * n + hexdigit; 792ba371819SOllivier Robert } 793ba371819SOllivier Robert *result = n; 794ba371819SOllivier Robert return (n); 795ba371819SOllivier Robert } 796ba371819SOllivier Robert 7979c2daa00SOllivier Robert static int 7989c2daa00SOllivier Robert neol_atoi_len(const char str[], 799ba371819SOllivier Robert int *result, 800ba371819SOllivier Robert int maxlen) 801ba371819SOllivier Robert { 802ba371819SOllivier Robert int digit; 803ba371819SOllivier Robert int i; 804ba371819SOllivier Robert int n = 0; 805ba371819SOllivier Robert 8062b15cb3dSCy Schubert for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++) 807ba371819SOllivier Robert { 808ba371819SOllivier Robert digit = str[i] - '0'; 809ba371819SOllivier Robert n = 10 * n + digit; 810ba371819SOllivier Robert } 811ba371819SOllivier Robert *result = n; 812ba371819SOllivier Robert return (n); 813ba371819SOllivier Robert } 814ba371819SOllivier Robert 815ba371819SOllivier Robert /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. 816ba371819SOllivier Robert * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 817ba371819SOllivier Robert * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. 818ba371819SOllivier Robert * 819ba371819SOllivier Robert * [For the Julian calendar (which was used in Russia before 1917, 820ba371819SOllivier Robert * Britain & colonies before 1752, anywhere else before 1582, 821ba371819SOllivier Robert * and is still in use by some communities) leave out the 822ba371819SOllivier Robert * -year/100+year/400 terms, and add 10.] 823ba371819SOllivier Robert * 824ba371819SOllivier Robert * This algorithm was first published by Gauss (I think). 825ba371819SOllivier Robert * 826ba371819SOllivier Robert * WARNING: this function will overflow on 2106-02-07 06:28:16 on 827ba371819SOllivier Robert * machines were long is 32-bit! (However, as time_t is signed, we 828ba371819SOllivier Robert * will already get problems at other places on 2038-01-19 03:14:08) 829ba371819SOllivier Robert */ 8309c2daa00SOllivier Robert static unsigned long 8319c2daa00SOllivier Robert neol_mktime(int year, 832ba371819SOllivier Robert int mon, 833ba371819SOllivier Robert int day, 834ba371819SOllivier Robert int hour, 835ba371819SOllivier Robert int min, 836ba371819SOllivier Robert int sec) 837ba371819SOllivier Robert { 838ba371819SOllivier Robert if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ 839ba371819SOllivier Robert mon += 12; /* Puts Feb last since it has leap day */ 840ba371819SOllivier Robert year -= 1; 841ba371819SOllivier Robert } 842ba371819SOllivier Robert return ((( 843ba371819SOllivier Robert (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + 844ba371819SOllivier Robert year*365 - 719499 845ba371819SOllivier Robert )*24 + hour /* now have hours */ 846ba371819SOllivier Robert )*60 + min /* now have minutes */ 847ba371819SOllivier Robert )*60 + sec; /* finally seconds */ 848ba371819SOllivier Robert } 849ba371819SOllivier Robert 8509c2daa00SOllivier Robert static void 8519c2daa00SOllivier Robert neol_localtime(unsigned long utc, 852ba371819SOllivier Robert int* year, 853ba371819SOllivier Robert int* month, 854ba371819SOllivier Robert int* day, 855ba371819SOllivier Robert int* hour, 8569c2daa00SOllivier Robert int* min, 8579c2daa00SOllivier Robert int* sec) 858ba371819SOllivier Robert { 8599c2daa00SOllivier Robert *sec = utc % 60; 8609c2daa00SOllivier Robert utc /= 60; 8619c2daa00SOllivier Robert *min = utc % 60; 8629c2daa00SOllivier Robert utc /= 60; 8639c2daa00SOllivier Robert *hour = utc % 24; 8649c2daa00SOllivier Robert utc /= 24; 865ba371819SOllivier Robert 866ba371819SOllivier Robert /* JDN Date 1/1/1970 */ 8679c2daa00SOllivier Robert neol_jdn_to_ymd(utc + 2440588L, year, month, day); 868ba371819SOllivier Robert } 869ba371819SOllivier Robert 8709c2daa00SOllivier Robert static void 8719c2daa00SOllivier Robert neol_jdn_to_ymd(unsigned long jdn, 872ba371819SOllivier Robert int *yy, 873ba371819SOllivier Robert int *mm, 874ba371819SOllivier Robert int *dd) 875ba371819SOllivier Robert { 876ba371819SOllivier Robert unsigned long x, z, m, d, y; 877ba371819SOllivier Robert unsigned long daysPer400Years = 146097UL; 878ba371819SOllivier Robert unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; 879ba371819SOllivier Robert 880ba371819SOllivier Robert x = jdn + 68569UL; 881ba371819SOllivier Robert z = 4UL * x / daysPer400Years; 882ba371819SOllivier Robert x = x - (daysPer400Years * z + 3UL) / 4UL; 883ba371819SOllivier Robert y = 4000UL * (x + 1) / fudgedDaysPer4000Years; 884ba371819SOllivier Robert x = x - 1461UL * y / 4UL + 31UL; 885ba371819SOllivier Robert m = 80UL * x / 2447UL; 886ba371819SOllivier Robert d = x - 2447UL * m / 80UL; 887ba371819SOllivier Robert x = m / 11UL; 888ba371819SOllivier Robert m = m + 2UL - 12UL * x; 889ba371819SOllivier Robert y = 100UL * (z - 49UL) + y + x; 890ba371819SOllivier Robert 891ba371819SOllivier Robert *yy = (int)y; 892ba371819SOllivier Robert *mm = (int)m; 893ba371819SOllivier Robert *dd = (int)d; 894ba371819SOllivier Robert } 895ba371819SOllivier Robert 8969c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE) 897ba371819SOllivier Robert static int 898ba371819SOllivier Robert neol_query_firmware(int fd, 899ba371819SOllivier Robert int unit, 900ba371819SOllivier Robert char *firmware, 9012b15cb3dSCy Schubert size_t maxlen) 902ba371819SOllivier Robert { 9039c2daa00SOllivier Robert char tmpbuf[256]; 9042b15cb3dSCy Schubert size_t len; 905ba371819SOllivier Robert int lastsearch; 906ba371819SOllivier Robert unsigned char c; 907ba371819SOllivier Robert int last_c_was_crlf; 908ba371819SOllivier Robert int last_crlf_conv_len; 909ba371819SOllivier Robert int init; 9109c2daa00SOllivier Robert int read_errors; 911ba371819SOllivier Robert int flag = 0; 9129c2daa00SOllivier Robert int chars_read; 913ba371819SOllivier Robert 914ba371819SOllivier Robert /* wait a little bit */ 9159c2daa00SOllivier Robert sleep(1); 916ba371819SOllivier Robert if(-1 != write(fd, "V", 1)) 917ba371819SOllivier Robert { 918ba371819SOllivier Robert /* wait a little bit */ 9199c2daa00SOllivier Robert sleep(1); 920ba371819SOllivier Robert memset(tmpbuf, 0x00, sizeof(tmpbuf)); 921ba371819SOllivier Robert 922ba371819SOllivier Robert len = 0; 923ba371819SOllivier Robert lastsearch = 0; 924ba371819SOllivier Robert last_c_was_crlf = 0; 925ba371819SOllivier Robert last_crlf_conv_len = 0; 926ba371819SOllivier Robert init = 1; 9279c2daa00SOllivier Robert read_errors = 0; 9289c2daa00SOllivier Robert chars_read = 0; 929ba371819SOllivier Robert for(;;) 930ba371819SOllivier Robert { 9319c2daa00SOllivier Robert if(read_errors > 5) 932ba371819SOllivier Robert { 933ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); 9342b15cb3dSCy Schubert strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf)); 935ba371819SOllivier Robert break; 936ba371819SOllivier Robert } 9379c2daa00SOllivier Robert if(chars_read > 500) 9389c2daa00SOllivier Robert { 9399c2daa00SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit); 9402b15cb3dSCy Schubert strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf)); 9419c2daa00SOllivier Robert break; 9429c2daa00SOllivier Robert } 943ba371819SOllivier Robert if(-1 == read(fd, &c, 1)) 944ba371819SOllivier Robert { 9459c2daa00SOllivier Robert if(EAGAIN != errno) 9469c2daa00SOllivier Robert { 9472b15cb3dSCy Schubert msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit); 9489c2daa00SOllivier Robert read_errors++; 9499c2daa00SOllivier Robert } 9509c2daa00SOllivier Robert else 9519c2daa00SOllivier Robert { 9529c2daa00SOllivier Robert sleep(1); 9539c2daa00SOllivier Robert } 954ba371819SOllivier Robert continue; 955ba371819SOllivier Robert } 9569c2daa00SOllivier Robert else 9579c2daa00SOllivier Robert { 9589c2daa00SOllivier Robert chars_read++; 9599c2daa00SOllivier Robert } 9609c2daa00SOllivier Robert 961ba371819SOllivier Robert if(init) 962ba371819SOllivier Robert { 963ba371819SOllivier Robert if(0xA9 != c) /* wait for (c) char in input stream */ 964ba371819SOllivier Robert continue; 965ba371819SOllivier Robert 9662b15cb3dSCy Schubert strlcpy(tmpbuf, "(c)", sizeof(tmpbuf)); 967ba371819SOllivier Robert len = 3; 968ba371819SOllivier Robert init = 0; 969ba371819SOllivier Robert continue; 970ba371819SOllivier Robert } 971ba371819SOllivier Robert 9729c2daa00SOllivier Robert #if 0 9739c2daa00SOllivier Robert msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); 9749c2daa00SOllivier Robert #endif 9759c2daa00SOllivier Robert 976ba371819SOllivier Robert if(0x0A == c || 0x0D == c) 977ba371819SOllivier Robert { 978ba371819SOllivier Robert if(last_c_was_crlf) 979ba371819SOllivier Robert { 980ba371819SOllivier Robert char *ptr; 981ba371819SOllivier Robert ptr = strstr(&tmpbuf[lastsearch], "S/N"); 982ba371819SOllivier Robert if(NULL != ptr) 983ba371819SOllivier Robert { 984ba371819SOllivier Robert tmpbuf[last_crlf_conv_len] = 0; 985ba371819SOllivier Robert flag = 1; 986ba371819SOllivier Robert break; 987ba371819SOllivier Robert } 988ba371819SOllivier Robert /* convert \n to / */ 989ba371819SOllivier Robert last_crlf_conv_len = len; 990ba371819SOllivier Robert tmpbuf[len++] = ' '; 991ba371819SOllivier Robert tmpbuf[len++] = '/'; 992ba371819SOllivier Robert tmpbuf[len++] = ' '; 993ba371819SOllivier Robert lastsearch = len; 994ba371819SOllivier Robert } 995ba371819SOllivier Robert last_c_was_crlf = 1; 996ba371819SOllivier Robert } 997ba371819SOllivier Robert else 998ba371819SOllivier Robert { 999ba371819SOllivier Robert last_c_was_crlf = 0; 1000ba371819SOllivier Robert if(0x00 != c) 10019c2daa00SOllivier Robert tmpbuf[len++] = (char) c; 1002ba371819SOllivier Robert } 1003ba371819SOllivier Robert tmpbuf[len] = '\0'; 1004ba371819SOllivier Robert if (len > sizeof(tmpbuf)-5) 1005ba371819SOllivier Robert break; 1006ba371819SOllivier Robert } 1007ba371819SOllivier Robert } 1008ba371819SOllivier Robert else 1009ba371819SOllivier Robert { 1010ba371819SOllivier Robert msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); 10112b15cb3dSCy Schubert strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf)); 1012ba371819SOllivier Robert } 10132b15cb3dSCy Schubert if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen) 10142b15cb3dSCy Schubert strlcpy(firmware, "buffer too small", maxlen); 1015ba371819SOllivier Robert 1016ba371819SOllivier Robert if(flag) 1017ba371819SOllivier Robert { 1018ba371819SOllivier Robert NLOG(NLOG_CLOCKINFO) 1019ba371819SOllivier Robert msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); 10202b15cb3dSCy Schubert 10212b15cb3dSCy Schubert if(strstr(firmware, "/R2")) 10222b15cb3dSCy Schubert { 10232b15cb3dSCy Schubert msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit); 10242b15cb3dSCy Schubert } 10252b15cb3dSCy Schubert 1026ba371819SOllivier Robert } 1027ba371819SOllivier Robert 1028ba371819SOllivier Robert return (flag); 1029ba371819SOllivier Robert } 1030ba371819SOllivier Robert 10319c2daa00SOllivier Robert static int 10329c2daa00SOllivier Robert neol_check_firmware(int unit, 10339c2daa00SOllivier Robert const char *firmware, 10349c2daa00SOllivier Robert char *firmwaretag) 10359c2daa00SOllivier Robert { 10369c2daa00SOllivier Robert char *ptr; 10379c2daa00SOllivier Robert 10389c2daa00SOllivier Robert *firmwaretag = '?'; 10399c2daa00SOllivier Robert ptr = strstr(firmware, "NDF:"); 10409c2daa00SOllivier Robert if(NULL != ptr) 10419c2daa00SOllivier Robert { 10429c2daa00SOllivier Robert if((strlen(firmware) - strlen(ptr)) >= 7) 10439c2daa00SOllivier Robert { 10449c2daa00SOllivier Robert if(':' == *(ptr+5) && '*' == *(ptr+6)) 10459c2daa00SOllivier Robert *firmwaretag = *(ptr+4); 10469c2daa00SOllivier Robert } 10479c2daa00SOllivier Robert } 10489c2daa00SOllivier Robert 10499c2daa00SOllivier Robert if('A' != *firmwaretag) 10509c2daa00SOllivier Robert { 10519c2daa00SOllivier Robert msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag); 10529c2daa00SOllivier Robert return (0); 10539c2daa00SOllivier Robert } 10549c2daa00SOllivier Robert 10559c2daa00SOllivier Robert return (1); 10569c2daa00SOllivier Robert } 10579c2daa00SOllivier Robert #endif 10589c2daa00SOllivier Robert 1059ba371819SOllivier Robert #else 1060*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 1061ba371819SOllivier Robert #endif /* REFCLOCK */ 1062ba371819SOllivier Robert 1063ba371819SOllivier Robert /* 1064ba371819SOllivier Robert * History: 1065ba371819SOllivier Robert * refclock_neoclock4x.c 1066ba371819SOllivier Robert * 1067ba371819SOllivier Robert * 2002/04/27 cjh 1068ba371819SOllivier Robert * Revision 1.0 first release 1069ba371819SOllivier Robert * 10709c2daa00SOllivier Robert * 2002/07/15 cjh 1071ba371819SOllivier Robert * preparing for bitkeeper reposity 1072ba371819SOllivier Robert * 10739c2daa00SOllivier Robert * 2002/09/09 cjh 10749c2daa00SOllivier Robert * Revision 1.1 10759c2daa00SOllivier Robert * - don't assume sprintf returns an int anymore 10769c2daa00SOllivier Robert * - change the way the firmware version is read 10779c2daa00SOllivier Robert * - some customers would like to put a device called 10789c2daa00SOllivier Robert * data diode to the NeoClock4X device to disable 10799c2daa00SOllivier Robert * the write line. We need to now the firmware 10809c2daa00SOllivier Robert * version even in this case. We made a compile time 10819c2daa00SOllivier Robert * definition in this case. The code was previously 10829c2daa00SOllivier Robert * only available on request. 10839c2daa00SOllivier Robert * 10849c2daa00SOllivier Robert * 2003/01/08 cjh 10859c2daa00SOllivier Robert * Revision 1.11 10869c2daa00SOllivier Robert * - changing xprinf to xnprinf to avoid buffer overflows 10879c2daa00SOllivier Robert * - change some logic 10889c2daa00SOllivier Robert * - fixed memory leaks if drivers can't initialize 10899c2daa00SOllivier Robert * 10909c2daa00SOllivier Robert * 2003/01/10 cjh 10919c2daa00SOllivier Robert * Revision 1.12 10929c2daa00SOllivier Robert * - replaced ldiv 10939c2daa00SOllivier Robert * - add code to support FreeBSD 10949c2daa00SOllivier Robert * 10959c2daa00SOllivier Robert * 2003/07/07 cjh 10969c2daa00SOllivier Robert * Revision 1.13 10979c2daa00SOllivier Robert * - fix reporting of clock status 10989c2daa00SOllivier Robert * changes. previously a bad clock 10999c2daa00SOllivier Robert * status was never reset. 1100ea906c41SOllivier Robert * 1101ea906c41SOllivier Robert * 2004/04/07 cjh 1102ea906c41SOllivier Robert * Revision 1.14 1103ea906c41SOllivier Robert * - open serial port in a way 1104ea906c41SOllivier Robert * AIX and some other OS can 1105ea906c41SOllivier Robert * handle much better 1106ea906c41SOllivier Robert * 1107ea906c41SOllivier Robert * 2006/01/11 cjh 1108ea906c41SOllivier Robert * Revision 1.15 1109ea906c41SOllivier Robert * - remove some unsued #ifdefs 1110ea906c41SOllivier Robert * - fix nsec calculation, closes #499 1111ea906c41SOllivier Robert * 11122b15cb3dSCy Schubert * 2009/12/04 cjh 11132b15cb3dSCy Schubert * Revision 1.16 11142b15cb3dSCy Schubert * - change license to ntp COPYRIGHT notice. This should allow Debian 11152b15cb3dSCy Schubert * to add this refclock driver in further releases. 11162b15cb3dSCy Schubert * - detect R2 hardware 11172b15cb3dSCy Schubert * 1118ba371819SOllivier Robert */ 11192b15cb3dSCy Schubert 11202b15cb3dSCy Schubert 11212b15cb3dSCy Schubert 11222b15cb3dSCy Schubert 11232b15cb3dSCy Schubert 11242b15cb3dSCy Schubert 1125