refclock_neoclock4x.c [plain text]
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_control.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#if defined HAVE_SYS_MODEM_H
# include <sys/modem.h>
# ifndef __QNXNTO__
# define TIOCMSET MCSETA
# define TIOCMGET MCGETA
# define TIOCM_RTS MRTS
# endif
#endif
#ifdef HAVE_TERMIOS_H
# ifdef TERMIOS_NEEDS__SVID3
# define _SVID3
# endif
# include <termios.h>
# ifdef TERMIOS_NEEDS__SVID3
# undef _SVID3
# endif
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if 0
#define NTP_PRE_420
#endif
#if 0
#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
#endif
#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
#define NEOCLOCK4X_TIMECODELEN 37
#define NEOCLOCK4X_OFFSET_SERIAL 3
#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
#define NEOCLOCK4X_OFFSET_DAY 12
#define NEOCLOCK4X_OFFSET_MONTH 14
#define NEOCLOCK4X_OFFSET_YEAR 16
#define NEOCLOCK4X_OFFSET_HOUR 18
#define NEOCLOCK4X_OFFSET_MINUTE 20
#define NEOCLOCK4X_OFFSET_SECOND 22
#define NEOCLOCK4X_OFFSET_HSEC 24
#define NEOCLOCK4X_OFFSET_DOW 26
#define NEOCLOCK4X_OFFSET_TIMESOURCE 28
#define NEOCLOCK4X_OFFSET_DSTSTATUS 29
#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
#define NEOCLOCK4X_OFFSET_ANTENNA1 31
#define NEOCLOCK4X_OFFSET_ANTENNA2 33
#define NEOCLOCK4X_OFFSET_CRC 35
#define NEOCLOCK4X_DRIVER_VERSION "1.15 (2006-01-11)"
#define NSEC_TO_MILLI 1000000
struct neoclock4x_unit {
l_fp laststamp;
short unit;
u_long polled;
char leap_status;
int recvnow;
char firmware[80];
char firmwaretag;
char serial[7];
char radiosignal[4];
char timesource;
char dststatus;
char quarzstatus;
int antenna1;
int antenna2;
int utc_year;
int utc_month;
int utc_day;
int utc_hour;
int utc_minute;
int utc_second;
int utc_msec;
};
static int neoclock4x_start (int, struct peer *);
static void neoclock4x_shutdown (int, struct peer *);
static void neoclock4x_receive (struct recvbuf *);
static void neoclock4x_poll (int, struct peer *);
static void neoclock4x_control (int, struct refclockstat *, struct refclockstat *, struct peer *);
static int neol_atoi_len (const char str[], int *, int);
static int neol_hexatoi_len (const char str[], int *, int);
static void neol_jdn_to_ymd (unsigned long, int *, int *, int *);
static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*);
static unsigned long neol_mktime (int, int, int, int, int, int);
#if !defined(NEOCLOCK4X_FIRMWARE)
static int neol_query_firmware (int, int, char *, int);
static int neol_check_firmware (int, const char*, char *);
#endif
struct refclock refclock_neoclock4x = {
neoclock4x_start,
neoclock4x_shutdown,
neoclock4x_poll,
neoclock4x_control,
noentry,
noentry,
NOFLAGS
};
static int
neoclock4x_start(int unit,
struct peer *peer)
{
struct neoclock4x_unit *up;
struct refclockproc *pp;
int fd;
char dev[20];
int sl232;
#if defined(HAVE_TERMIOS)
struct termios termsettings;
#endif
#if !defined(NEOCLOCK4X_FIRMWARE)
int tries;
#endif
(void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
fd = refclock_open(dev, B2400, LDISC_STD);
if(fd <= 0)
{
return (0);
}
#if defined(HAVE_TERMIOS)
#if 1
if(tcgetattr(fd, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
(void) close(fd);
return (0);
}
termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
termsettings.c_oflag = 0;
termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
(void)cfsetispeed(&termsettings, (u_int)B2400);
(void)cfsetospeed(&termsettings, (u_int)B2400);
if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
(void) close(fd);
return (0);
}
#else
if(tcgetattr(fd, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
(void) close(fd);
return (0);
}
termsettings.c_cflag &= ~PARENB;
termsettings.c_cflag |= CSTOPB;
termsettings.c_cflag &= ~CSIZE;
termsettings.c_cflag |= CS8;
if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
(void) close(fd);
return (0);
}
#endif
#elif defined(HAVE_SYSV_TTYS)
if(ioctl(fd, TCGETA, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
(void) close(fd);
return (0);
}
termsettings.c_cflag &= ~PARENB;
termsettings.c_cflag |= CSTOPB;
termsettings.c_cflag &= ~CSIZE;
termsettings.c_cflag |= CS8;
if(ioctl(fd, TCSETA, &termsettings) < 0)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
(void) close(fd);
return (0);
}
#else
msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
(void) close(fd);
return (0);
#endif
#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
(void) close(fd);
return (0);
}
#ifdef TIOCM_RTS
sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;
#else
sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;
#endif
if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
(void) close(fd);
return (0);
}
#else
msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
unit);
(void) close(fd);
return (0);
#endif
up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
if(!(up))
{
msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
(void) close(fd);
return (0);
}
memset((char *)up, 0, sizeof(struct neoclock4x_unit));
pp = peer->procptr;
pp->clockdesc = "NeoClock4X";
pp->unitptr = (caddr_t)up;
pp->io.clock_recv = neoclock4x_receive;
pp->io.srcclock = (caddr_t)peer;
pp->io.datalen = 0;
pp->io.fd = fd;
pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
peer->precision = -10;
peer->burst = NSTAGE;
memcpy((char *)&pp->refid, "neol", 4);
up->leap_status = 0;
up->unit = unit;
strcpy(up->firmware, "?");
up->firmwaretag = '?';
strcpy(up->serial, "?");
strcpy(up->radiosignal, "?");
up->timesource = '?';
up->dststatus = '?';
up->quarzstatus = '?';
up->antenna1 = -1;
up->antenna2 = -1;
up->utc_year = 0;
up->utc_month = 0;
up->utc_day = 0;
up->utc_hour = 0;
up->utc_minute = 0;
up->utc_second = 0;
up->utc_msec = 0;
#if defined(NEOCLOCK4X_FIRMWARE)
#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
up->firmwaretag = 'A';
#else
msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
unit);
(void) close(fd);
pp->io.fd = -1;
free(pp->unitptr);
pp->unitptr = NULL;
return (0);
#endif
#else
for(tries=0; tries < 5; tries++)
{
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
sleep(3);
if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
{
break;
}
}
if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
{
(void) close(fd);
pp->io.fd = -1;
free(pp->unitptr);
pp->unitptr = NULL;
return (0);
}
#endif
if(!io_addclock(&pp->io))
{
msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
(void) close(fd);
pp->io.fd = -1;
free(pp->unitptr);
pp->unitptr = NULL;
return (0);
}
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
return (1);
}
static void
neoclock4x_shutdown(int unit,
struct peer *peer)
{
struct neoclock4x_unit *up;
struct refclockproc *pp;
int sl232;
if(NULL != peer)
{
pp = peer->procptr;
if(pp != NULL)
{
up = (struct neoclock4x_unit *)pp->unitptr;
if(up != NULL)
{
if(-1 != pp->io.fd)
{
#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
unit);
}
#ifdef TIOCM_RTS
sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
#else
sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
#endif
if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
unit);
}
#endif
io_closeclock(&pp->io);
}
free(up);
pp->unitptr = NULL;
}
}
}
msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
}
static void
neoclock4x_receive(struct recvbuf *rbufp)
{
struct neoclock4x_unit *up;
struct refclockproc *pp;
struct peer *peer;
unsigned long calc_utc;
int day;
int month;
int c;
int dsec;
unsigned char calc_chksum;
int recv_chksum;
peer = (struct peer *)rbufp->recv_srcclock;
pp = peer->procptr;
up = (struct neoclock4x_unit *)pp->unitptr;
if(0 == up->recvnow)
return;
up->recvnow = 0;
pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
pp->leap = LEAP_NOWARNING;
if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
{
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
refclock_report(peer, CEVNT_BADREPLY);
return;
}
neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
calc_chksum = 0;
for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
{
calc_chksum += pp->a_lastcode[c];
}
if(recv_chksum != calc_chksum)
{
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
up->unit, pp->a_lastcode);
refclock_report(peer, CEVNT_BADREPLY);
return;
}
up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
if(0==(pp->sloppyclockflag & CLK_FLAG2))
{
if('I' != up->quarzstatus)
{
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
up->unit, pp->a_lastcode);
pp->leap = LEAP_NOTINSYNC;
refclock_report(peer, CEVNT_BADDATE);
return;
}
}
if('I' != up->quarzstatus)
{
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
up->unit, pp->a_lastcode);
}
up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
if(0==(pp->sloppyclockflag & CLK_FLAG2))
{
if('A' != up->timesource)
{
if(0==(pp->sloppyclockflag & CLK_FLAG1))
{
refclock_report(peer, CEVNT_BADTIME);
pp->leap = LEAP_NOTINSYNC;
return;
}
}
}
if(pp->sloppyclockflag & CLK_FLAG4)
{
msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
up->unit, pp->a_lastcode);
}
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
#if defined(NTP_PRE_420)
pp->msec = dsec * 10;
#else
pp->nsec = dsec * 10 * NSEC_TO_MILLI;
#endif
memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
up->radiosignal[3] = 0;
memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
up->serial[6] = 0;
up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
if((pp->hour < 0) || (pp->hour > 23) ||
(pp->minute < 0) || (pp->minute > 59) ||
(pp->second < 0) || (pp->second > 60) ||
(day < 1) || (day > 31) ||
(month < 1) || (month > 12) ||
(pp->year < 0) || (pp->year > 99)) {
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
up->unit, pp->a_lastcode);
refclock_report(peer, CEVNT_BADDATE);
return;
}
pp->year += 2000;
calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
calc_utc -= 3600;
if('S' == up->dststatus)
calc_utc -= 3600;
neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
pp->day = ymd2yd(pp->year, month, day);
pp->leap = 0;
if(pp->sloppyclockflag & CLK_FLAG4)
{
msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
up->unit,
pp->year, month, day,
pp->hour, pp->minute, pp->second,
#if defined(NTP_PRE_420)
pp->msec
#else
pp->nsec/NSEC_TO_MILLI
#endif
);
}
up->utc_year = pp->year;
up->utc_month = month;
up->utc_day = day;
up->utc_hour = pp->hour;
up->utc_minute = pp->minute;
up->utc_second = pp->second;
#if defined(NTP_PRE_420)
up->utc_msec = pp->msec;
#else
up->utc_msec = pp->nsec/NSEC_TO_MILLI;
#endif
if(!refclock_process(pp))
{
NLOG(NLOG_CLOCKEVENT)
msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
refclock_report(peer, CEVNT_FAULT);
return;
}
refclock_receive(peer);
refclock_report(peer, CEVNT_NOMINAL);
record_clock_stats(&peer->srcadr, pp->a_lastcode);
}
static void
neoclock4x_poll(int unit,
struct peer *peer)
{
struct neoclock4x_unit *up;
struct refclockproc *pp;
pp = peer->procptr;
up = (struct neoclock4x_unit *)pp->unitptr;
pp->polls++;
up->recvnow = 1;
}
static void
neoclock4x_control(int unit,
struct refclockstat *in,
struct refclockstat *out,
struct peer *peer)
{
struct neoclock4x_unit *up;
struct refclockproc *pp;
if(NULL == peer)
{
msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
return;
}
pp = peer->procptr;
if(NULL == pp)
{
msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
return;
}
up = (struct neoclock4x_unit *)pp->unitptr;
if(NULL == up)
{
msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
return;
}
if(NULL != in)
{
if(in->haveflags & CLK_HAVETIME1)
{
pp->fudgetime1 = in->fudgetime1;
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
unit, pp->fudgetime1);
}
if(pp->sloppyclockflag & CLK_FLAG1)
{
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
}
else
{
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
}
}
if(NULL != out)
{
char *tt;
char tmpbuf[80];
out->kv_list = (struct ctl_var *)0;
out->type = REFCLK_NEOCLOCK4X;
snprintf(tmpbuf, sizeof(tmpbuf)-1,
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
up->utc_year, up->utc_month, up->utc_day,
up->utc_hour, up->utc_minute, up->utc_second,
up->utc_msec);
tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
tt = add_var(&out->kv_list, 40, RO|DEF);
snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
tt = add_var(&out->kv_list, 40, RO|DEF);
snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
tt = add_var(&out->kv_list, 40, RO|DEF);
snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
tt = add_var(&out->kv_list, 40, RO|DEF);
if('A' == up->timesource)
snprintf(tt, 39, "timesource=\"radio\"");
else if('C' == up->timesource)
snprintf(tt, 39, "timesource=\"quartz\"");
else
snprintf(tt, 39, "timesource=\"unknown\"");
tt = add_var(&out->kv_list, 40, RO|DEF);
if('I' == up->quarzstatus)
snprintf(tt, 39, "quartzstatus=\"synchronized\"");
else if('X' == up->quarzstatus)
snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
else
snprintf(tt, 39, "quartzstatus=\"unknown\"");
tt = add_var(&out->kv_list, 40, RO|DEF);
if('S' == up->dststatus)
snprintf(tt, 39, "dststatus=\"summer\"");
else if('W' == up->dststatus)
snprintf(tt, 39, "dststatus=\"winter\"");
else
snprintf(tt, 39, "dststatus=\"unknown\"");
tt = add_var(&out->kv_list, 80, RO|DEF);
snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
tt = add_var(&out->kv_list, 40, RO|DEF);
snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
tt = add_var(&out->kv_list, 80, RO|DEF);
snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
tt = add_var(&out->kv_list, 80, RO|DEF);
snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
}
}
static int
neol_hexatoi_len(const char str[],
int *result,
int maxlen)
{
int hexdigit;
int i;
int n = 0;
for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
{
hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
n = 16 * n + hexdigit;
}
*result = n;
return (n);
}
static int
neol_atoi_len(const char str[],
int *result,
int maxlen)
{
int digit;
int i;
int n = 0;
for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
{
digit = str[i] - '0';
n = 10 * n + digit;
}
*result = n;
return (n);
}
static unsigned long
neol_mktime(int year,
int mon,
int day,
int hour,
int min,
int sec)
{
if (0 >= (int) (mon -= 2)) {
mon += 12;
year -= 1;
}
return (((
(unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour
)*60 + min
)*60 + sec;
}
static void
neol_localtime(unsigned long utc,
int* year,
int* month,
int* day,
int* hour,
int* min,
int* sec)
{
*sec = utc % 60;
utc /= 60;
*min = utc % 60;
utc /= 60;
*hour = utc % 24;
utc /= 24;
neol_jdn_to_ymd(utc + 2440588L, year, month, day);
}
static void
neol_jdn_to_ymd(unsigned long jdn,
int *yy,
int *mm,
int *dd)
{
unsigned long x, z, m, d, y;
unsigned long daysPer400Years = 146097UL;
unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
x = jdn + 68569UL;
z = 4UL * x / daysPer400Years;
x = x - (daysPer400Years * z + 3UL) / 4UL;
y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
x = x - 1461UL * y / 4UL + 31UL;
m = 80UL * x / 2447UL;
d = x - 2447UL * m / 80UL;
x = m / 11UL;
m = m + 2UL - 12UL * x;
y = 100UL * (z - 49UL) + y + x;
*yy = (int)y;
*mm = (int)m;
*dd = (int)d;
}
#if !defined(NEOCLOCK4X_FIRMWARE)
static int
neol_query_firmware(int fd,
int unit,
char *firmware,
int maxlen)
{
char tmpbuf[256];
int len;
int lastsearch;
unsigned char c;
int last_c_was_crlf;
int last_crlf_conv_len;
int init;
int read_errors;
int flag = 0;
int chars_read;
sleep(1);
if(-1 != write(fd, "V", 1))
{
sleep(1);
memset(tmpbuf, 0x00, sizeof(tmpbuf));
len = 0;
lastsearch = 0;
last_c_was_crlf = 0;
last_crlf_conv_len = 0;
init = 1;
read_errors = 0;
chars_read = 0;
for(;;)
{
if(read_errors > 5)
{
msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
strcpy(tmpbuf, "unknown due to timeout");
break;
}
if(chars_read > 500)
{
msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
strcpy(tmpbuf, "unknown due to garbage input");
break;
}
if(-1 == read(fd, &c, 1))
{
if(EAGAIN != errno)
{
msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
read_errors++;
}
else
{
sleep(1);
}
continue;
}
else
{
chars_read++;
}
if(init)
{
if(0xA9 != c)
continue;
strcpy(tmpbuf, "(c)");
len = 3;
init = 0;
continue;
}
#if 0
msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
#endif
if(0x0A == c || 0x0D == c)
{
if(last_c_was_crlf)
{
char *ptr;
ptr = strstr(&tmpbuf[lastsearch], "S/N");
if(NULL != ptr)
{
tmpbuf[last_crlf_conv_len] = 0;
flag = 1;
break;
}
last_crlf_conv_len = len;
tmpbuf[len++] = ' ';
tmpbuf[len++] = '/';
tmpbuf[len++] = ' ';
lastsearch = len;
}
last_c_was_crlf = 1;
}
else
{
last_c_was_crlf = 0;
if(0x00 != c)
tmpbuf[len++] = (char) c;
}
tmpbuf[len] = '\0';
if(len > sizeof(tmpbuf)-5)
break;
}
}
else
{
msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
strcpy(tmpbuf, "unknown error");
}
strncpy(firmware, tmpbuf, maxlen);
firmware[maxlen] = '\0';
if(flag)
{
NLOG(NLOG_CLOCKINFO)
msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
}
return (flag);
}
static int
neol_check_firmware(int unit,
const char *firmware,
char *firmwaretag)
{
char *ptr;
*firmwaretag = '?';
ptr = strstr(firmware, "NDF:");
if(NULL != ptr)
{
if((strlen(firmware) - strlen(ptr)) >= 7)
{
if(':' == *(ptr+5) && '*' == *(ptr+6))
*firmwaretag = *(ptr+4);
}
}
if('A' != *firmwaretag)
{
msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
return (0);
}
return (1);
}
#endif
#else
int refclock_neoclock4x_bs;
#endif