Annotation of embedaddon/ntp/ntpd/refclock_neoclock4x.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  *
        !             3:  * Refclock_neoclock4x.c
        !             4:  * - NeoClock4X driver for DCF77 or FIA Timecode
        !             5:  *
        !             6:  * Date: 2009-12-04 v1.16
        !             7:  *
        !             8:  * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
        !             9:  * for details about the NeoClock4X device
        !            10:  *
        !            11:  */
        !            12: 
        !            13: #ifdef HAVE_CONFIG_H
        !            14: # include "config.h"
        !            15: #endif
        !            16: 
        !            17: #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
        !            18: 
        !            19: #include <unistd.h>
        !            20: #include <sys/time.h>
        !            21: #include <sys/types.h>
        !            22: #include <termios.h>
        !            23: #include <sys/ioctl.h>
        !            24: #include <ctype.h>
        !            25: 
        !            26: #include "ntpd.h"
        !            27: #include "ntp_io.h"
        !            28: #include "ntp_control.h"
        !            29: #include "ntp_refclock.h"
        !            30: #include "ntp_unixtime.h"
        !            31: #include "ntp_stdlib.h"
        !            32: 
        !            33: #if defined HAVE_SYS_MODEM_H
        !            34: # include <sys/modem.h>
        !            35: # ifndef __QNXNTO__
        !            36: #  define TIOCMSET MCSETA
        !            37: #  define TIOCMGET MCGETA
        !            38: #  define TIOCM_RTS MRTS
        !            39: # endif
        !            40: #endif
        !            41: 
        !            42: #ifdef HAVE_TERMIOS_H
        !            43: # ifdef TERMIOS_NEEDS__SVID3
        !            44: #  define _SVID3
        !            45: # endif
        !            46: # include <termios.h>
        !            47: # ifdef TERMIOS_NEEDS__SVID3
        !            48: #  undef _SVID3
        !            49: # endif
        !            50: #endif
        !            51: 
        !            52: #ifdef HAVE_SYS_IOCTL_H
        !            53: # include <sys/ioctl.h>
        !            54: #endif
        !            55: 
        !            56: /*
        !            57:  * NTP version 4.20 change the pp->msec field to pp->nsec.
        !            58:  * To allow to support older ntp versions with this sourcefile
        !            59:  * you can define NTP_PRE_420 to allow this driver to compile
        !            60:  * with ntp version back to 4.1.2.
        !            61:  *
        !            62:  */
        !            63: #if 0
        !            64: #define NTP_PRE_420
        !            65: #endif
        !            66: 
        !            67: /*
        !            68:  * If you want the driver for whatever reason to not use
        !            69:  * the TX line to send anything to your NeoClock4X
        !            70:  * device you must tell the NTP refclock driver which
        !            71:  * firmware you NeoClock4X device uses.
        !            72:  *
        !            73:  * If you want to enable this feature change the "#if 0"
        !            74:  * line to "#if 1" and make sure that the defined firmware
        !            75:  * matches the firmware off your NeoClock4X receiver!
        !            76:  *
        !            77:  */
        !            78: 
        !            79: #if 0
        !            80: #define NEOCLOCK4X_FIRMWARE                NEOCLOCK4X_FIRMWARE_VERSION_A
        !            81: #endif
        !            82: 
        !            83: /* at this time only firmware version A is known */
        !            84: #define NEOCLOCK4X_FIRMWARE_VERSION_A      'A'
        !            85: 
        !            86: #define NEOCLOCK4X_TIMECODELEN 37
        !            87: 
        !            88: #define NEOCLOCK4X_OFFSET_SERIAL            3
        !            89: #define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
        !            90: #define NEOCLOCK4X_OFFSET_DAY              12
        !            91: #define NEOCLOCK4X_OFFSET_MONTH            14
        !            92: #define NEOCLOCK4X_OFFSET_YEAR             16
        !            93: #define NEOCLOCK4X_OFFSET_HOUR             18
        !            94: #define NEOCLOCK4X_OFFSET_MINUTE           20
        !            95: #define NEOCLOCK4X_OFFSET_SECOND           22
        !            96: #define NEOCLOCK4X_OFFSET_HSEC             24
        !            97: #define NEOCLOCK4X_OFFSET_DOW              26
        !            98: #define NEOCLOCK4X_OFFSET_TIMESOURCE       28
        !            99: #define NEOCLOCK4X_OFFSET_DSTSTATUS        29
        !           100: #define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
        !           101: #define NEOCLOCK4X_OFFSET_ANTENNA1         31
        !           102: #define NEOCLOCK4X_OFFSET_ANTENNA2         33
        !           103: #define NEOCLOCK4X_OFFSET_CRC              35
        !           104: 
        !           105: #define NEOCLOCK4X_DRIVER_VERSION          "1.16 (2009-12-04)"
        !           106: 
        !           107: #define NSEC_TO_MILLI                      1000000
        !           108: 
        !           109: struct neoclock4x_unit {
        !           110:   l_fp laststamp;      /* last receive timestamp */
        !           111:   short        unit;           /* NTP refclock unit number */
        !           112:   u_long polled;       /* flag to detect noreplies */
        !           113:   char leap_status;    /* leap second flag */
        !           114:   int  recvnow;
        !           115: 
        !           116:   char  firmware[80];
        !           117:   char  firmwaretag;
        !           118:   char  serial[7];
        !           119:   char  radiosignal[4];
        !           120:   char  timesource;
        !           121:   char  dststatus;
        !           122:   char  quarzstatus;
        !           123:   int   antenna1;
        !           124:   int   antenna2;
        !           125:   int   utc_year;
        !           126:   int   utc_month;
        !           127:   int   utc_day;
        !           128:   int   utc_hour;
        !           129:   int   utc_minute;
        !           130:   int   utc_second;
        !           131:   int   utc_msec;
        !           132: };
        !           133: 
        !           134: static int     neoclock4x_start        (int, struct peer *);
        !           135: static void    neoclock4x_shutdown     (int, struct peer *);
        !           136: static void    neoclock4x_receive      (struct recvbuf *);
        !           137: static void    neoclock4x_poll         (int, struct peer *);
        !           138: static void    neoclock4x_control      (int, struct refclockstat *, struct refclockstat *, struct peer *);
        !           139: 
        !           140: static int      neol_atoi_len           (const char str[], int *, int);
        !           141: static int      neol_hexatoi_len        (const char str[], int *, int);
        !           142: static void     neol_jdn_to_ymd         (unsigned long, int *, int *, int *);
        !           143: static void     neol_localtime          (unsigned long, int* , int*, int*, int*, int*, int*);
        !           144: static unsigned long neol_mktime        (int, int, int, int, int, int);
        !           145: #if !defined(NEOCLOCK4X_FIRMWARE)
        !           146: static int      neol_query_firmware     (int, int, char *, int);
        !           147: static int      neol_check_firmware     (int, const char*, char *);
        !           148: #endif
        !           149: 
        !           150: struct refclock refclock_neoclock4x = {
        !           151:   neoclock4x_start,    /* start up driver */
        !           152:   neoclock4x_shutdown, /* shut down driver */
        !           153:   neoclock4x_poll,     /* transmit poll message */
        !           154:   neoclock4x_control,
        !           155:   noentry,             /* initialize driver (not used) */
        !           156:   noentry,             /* not used */
        !           157:   NOFLAGS                      /* not used */
        !           158: };
        !           159: 
        !           160: static int
        !           161: neoclock4x_start(int unit,
        !           162:                 struct peer *peer)
        !           163: {
        !           164:   struct neoclock4x_unit *up;
        !           165:   struct refclockproc *pp;
        !           166:   int fd;
        !           167:   char dev[20];
        !           168:   int sl232;
        !           169: #if defined(HAVE_TERMIOS)
        !           170:   struct termios termsettings;
        !           171: #endif
        !           172: #if !defined(NEOCLOCK4X_FIRMWARE)
        !           173:   int tries;
        !           174: #endif
        !           175: 
        !           176:   (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
        !           177: 
        !           178:   /* LDISC_STD, LDISC_RAW
        !           179:    * Open serial port. Use CLK line discipline, if available.
        !           180:    */
        !           181:   fd = refclock_open(dev, B2400, LDISC_STD);
        !           182:   if(fd <= 0)
        !           183:     {
        !           184:       return (0);
        !           185:     }
        !           186: 
        !           187: #if defined(HAVE_TERMIOS)
        !           188: 
        !           189: #if 1
        !           190:   if(tcgetattr(fd, &termsettings) < 0)
        !           191:     {
        !           192:       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
        !           193:       (void) close(fd);
        !           194:       return (0);
        !           195:     }
        !           196: 
        !           197:   /* 2400 Baud 8N2 */
        !           198:   termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
        !           199:   termsettings.c_oflag = 0;
        !           200:   termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
        !           201:   (void)cfsetispeed(&termsettings, (u_int)B2400);
        !           202:   (void)cfsetospeed(&termsettings, (u_int)B2400);
        !           203: 
        !           204:   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
        !           205:     {
        !           206:       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
        !           207:       (void) close(fd);
        !           208:       return (0);
        !           209:     }
        !           210: 
        !           211: #else
        !           212:   if(tcgetattr(fd, &termsettings) < 0)
        !           213:     {
        !           214:       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
        !           215:       (void) close(fd);
        !           216:       return (0);
        !           217:     }
        !           218: 
        !           219:   /* 2400 Baud 8N2 */
        !           220:   termsettings.c_cflag &= ~PARENB;
        !           221:   termsettings.c_cflag |= CSTOPB;
        !           222:   termsettings.c_cflag &= ~CSIZE;
        !           223:   termsettings.c_cflag |= CS8;
        !           224: 
        !           225:   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
        !           226:     {
        !           227:       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
        !           228:       (void) close(fd);
        !           229:       return (0);
        !           230:     }
        !           231: #endif
        !           232: 
        !           233: #elif defined(HAVE_SYSV_TTYS)
        !           234:   if(ioctl(fd, TCGETA, &termsettings) < 0)
        !           235:     {
        !           236:       msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
        !           237:       (void) close(fd);
        !           238:       return (0);
        !           239:     }
        !           240: 
        !           241:   /* 2400 Baud 8N2 */
        !           242:   termsettings.c_cflag &= ~PARENB;
        !           243:   termsettings.c_cflag |= CSTOPB;
        !           244:   termsettings.c_cflag &= ~CSIZE;
        !           245:   termsettings.c_cflag |= CS8;
        !           246: 
        !           247:   if(ioctl(fd, TCSETA, &termsettings) < 0)
        !           248:     {
        !           249:       msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
        !           250:       (void) close(fd);
        !           251:       return (0);
        !           252:     }
        !           253: #else
        !           254:   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
        !           255:   (void) close(fd);
        !           256:   return (0);
        !           257: #endif
        !           258: 
        !           259: #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
        !           260:   /* turn on RTS, and DTR for power supply */
        !           261:   /* NeoClock4x is powered from serial line */
        !           262:   if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
        !           263:     {
        !           264:       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
        !           265:       (void) close(fd);
        !           266:       return (0);
        !           267:     }
        !           268: #ifdef TIOCM_RTS
        !           269:   sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;       /* turn on RTS, and DTR for power supply */
        !           270: #else
        !           271:   sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;       /* turn on RTS, and DTR for power supply */
        !           272: #endif
        !           273:   if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
        !           274:     {
        !           275:       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
        !           276:       (void) close(fd);
        !           277:       return (0);
        !           278:     }
        !           279: #else
        !           280:   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
        !           281:          unit);
        !           282:   (void) close(fd);
        !           283:   return (0);
        !           284: #endif
        !           285: 
        !           286:   up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
        !           287:   if(!(up))
        !           288:     {
        !           289:       msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
        !           290:       (void) close(fd);
        !           291:       return (0);
        !           292:     }
        !           293: 
        !           294:   memset((char *)up, 0, sizeof(struct neoclock4x_unit));
        !           295:   pp = peer->procptr;
        !           296:   pp->clockdesc = "NeoClock4X";
        !           297:   pp->unitptr = (caddr_t)up;
        !           298:   pp->io.clock_recv = neoclock4x_receive;
        !           299:   pp->io.srcclock = (caddr_t)peer;
        !           300:   pp->io.datalen = 0;
        !           301:   pp->io.fd = fd;
        !           302:   /*
        !           303:    * no fudge time is given by user!
        !           304:    * use 169.583333 ms to compensate the serial line delay
        !           305:    * formula is:
        !           306:    * 2400 Baud / 11 bit = 218.18 charaters per second
        !           307:    *  (NeoClock4X timecode len)
        !           308:    */
        !           309:   pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
        !           310: 
        !           311:   /*
        !           312:    * Initialize miscellaneous variables
        !           313:    */
        !           314:   peer->precision = -10;
        !           315:   peer->burst = NSTAGE;
        !           316:   memcpy((char *)&pp->refid, "neol", 4);
        !           317: 
        !           318:   up->leap_status = 0;
        !           319:   up->unit = unit;
        !           320:   strcpy(up->firmware, "?");
        !           321:   up->firmwaretag = '?';
        !           322:   strcpy(up->serial, "?");
        !           323:   strcpy(up->radiosignal, "?");
        !           324:   up->timesource  = '?';
        !           325:   up->dststatus   = '?';
        !           326:   up->quarzstatus = '?';
        !           327:   up->antenna1    = -1;
        !           328:   up->antenna2    = -1;
        !           329:   up->utc_year    = 0;
        !           330:   up->utc_month   = 0;
        !           331:   up->utc_day     = 0;
        !           332:   up->utc_hour    = 0;
        !           333:   up->utc_minute  = 0;
        !           334:   up->utc_second  = 0;
        !           335:   up->utc_msec    = 0;
        !           336: 
        !           337: #if defined(NEOCLOCK4X_FIRMWARE)
        !           338: #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
        !           339:   strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
        !           340:   up->firmwaretag = 'A';
        !           341: #else
        !           342:   msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
        !           343:          unit);
        !           344:   (void) close(fd);
        !           345:   pp->io.fd = -1;
        !           346:   free(pp->unitptr);
        !           347:   pp->unitptr = NULL;
        !           348:   return (0);
        !           349: #endif
        !           350: #else
        !           351:   for(tries=0; tries < 5; tries++)
        !           352:     {
        !           353:       NLOG(NLOG_CLOCKINFO)
        !           354:        msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
        !           355:       /* wait 3 seconds for receiver to power up */
        !           356:       sleep(3);
        !           357:       if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
        !           358:        {
        !           359:          break;
        !           360:        }
        !           361:     }
        !           362: 
        !           363:   /* can I handle this firmware version? */
        !           364:   if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
        !           365:     {
        !           366:       (void) close(fd);
        !           367:       pp->io.fd = -1;
        !           368:       free(pp->unitptr);
        !           369:       pp->unitptr = NULL;
        !           370:       return (0);
        !           371:     }
        !           372: #endif
        !           373: 
        !           374:   if(!io_addclock(&pp->io))
        !           375:     {
        !           376:       msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
        !           377:       (void) close(fd);
        !           378:       pp->io.fd = -1;
        !           379:       free(pp->unitptr);
        !           380:       pp->unitptr = NULL;
        !           381:       return (0);
        !           382:     }
        !           383: 
        !           384:   NLOG(NLOG_CLOCKINFO)
        !           385:     msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
        !           386: 
        !           387:   return (1);
        !           388: }
        !           389: 
        !           390: static void
        !           391: neoclock4x_shutdown(int unit,
        !           392:                   struct peer *peer)
        !           393: {
        !           394:   struct neoclock4x_unit *up;
        !           395:   struct refclockproc *pp;
        !           396:   int sl232;
        !           397: 
        !           398:   if(NULL != peer)
        !           399:     {
        !           400:       pp = peer->procptr;
        !           401:       if(pp != NULL)
        !           402:         {
        !           403:           up = (struct neoclock4x_unit *)pp->unitptr;
        !           404:           if(up != NULL)
        !           405:             {
        !           406:               if(-1 !=  pp->io.fd)
        !           407:                 {
        !           408: #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
        !           409:                   /* turn on RTS, and DTR for power supply */
        !           410:                   /* NeoClock4x is powered from serial line */
        !           411:                   if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
        !           412:                     {
        !           413:                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
        !           414:                               unit);
        !           415:                     }
        !           416: #ifdef TIOCM_RTS
        !           417:                   /* turn on RTS, and DTR for power supply */
        !           418:                   sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
        !           419: #else
        !           420:                   /* turn on RTS, and DTR for power supply */
        !           421:                   sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
        !           422: #endif
        !           423:                   if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
        !           424:                     {
        !           425:                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
        !           426:                               unit);
        !           427:                     }
        !           428: #endif
        !           429:                   io_closeclock(&pp->io);
        !           430:                 }
        !           431:               free(up);
        !           432:               pp->unitptr = NULL;
        !           433:             }
        !           434:         }
        !           435:     }
        !           436: 
        !           437:   msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
        !           438: 
        !           439:   NLOG(NLOG_CLOCKINFO)
        !           440:     msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
        !           441: }
        !           442: 
        !           443: static void
        !           444: neoclock4x_receive(struct recvbuf *rbufp)
        !           445: {
        !           446:   struct neoclock4x_unit *up;
        !           447:   struct refclockproc *pp;
        !           448:   struct peer *peer;
        !           449:   unsigned long calc_utc;
        !           450:   int day;
        !           451:   int month;   /* ddd conversion */
        !           452:   int c;
        !           453:   int dsec;
        !           454:   unsigned char calc_chksum;
        !           455:   int recv_chksum;
        !           456: 
        !           457:   peer = (struct peer *)rbufp->recv_srcclock;
        !           458:   pp = peer->procptr;
        !           459:   up = (struct neoclock4x_unit *)pp->unitptr;
        !           460: 
        !           461:   /* wait till poll interval is reached */
        !           462:   if(0 == up->recvnow)
        !           463:     return;
        !           464: 
        !           465:   /* reset poll interval flag */
        !           466:   up->recvnow = 0;
        !           467: 
        !           468:   /* read last received timecode */
        !           469:   pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
        !           470:   pp->leap = LEAP_NOWARNING;
        !           471: 
        !           472:   if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
        !           473:     {
        !           474:       NLOG(NLOG_CLOCKEVENT)
        !           475:        msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
        !           476:                up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
        !           477:       refclock_report(peer, CEVNT_BADREPLY);
        !           478:       return;
        !           479:     }
        !           480: 
        !           481:   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
        !           482: 
        !           483:   /* calculate checksum */
        !           484:   calc_chksum = 0;
        !           485:   for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
        !           486:     {
        !           487:       calc_chksum += pp->a_lastcode[c];
        !           488:     }
        !           489:   if(recv_chksum != calc_chksum)
        !           490:     {
        !           491:       NLOG(NLOG_CLOCKEVENT)
        !           492:        msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
        !           493:                up->unit, pp->a_lastcode);
        !           494:       refclock_report(peer, CEVNT_BADREPLY);
        !           495:       return;
        !           496:     }
        !           497: 
        !           498:   /* Allow synchronization even is quartz clock is
        !           499:    * never initialized.
        !           500:    * WARNING: This is dangerous!
        !           501:    */
        !           502:   up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
        !           503:   if(0==(pp->sloppyclockflag & CLK_FLAG2))
        !           504:     {
        !           505:       if('I' != up->quarzstatus)
        !           506:        {
        !           507:          NLOG(NLOG_CLOCKEVENT)
        !           508:            msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
        !           509:                    up->unit, pp->a_lastcode);
        !           510:          pp->leap = LEAP_NOTINSYNC;
        !           511:          refclock_report(peer, CEVNT_BADDATE);
        !           512:          return;
        !           513:        }
        !           514:     }
        !           515:   if('I' != up->quarzstatus)
        !           516:     {
        !           517:       NLOG(NLOG_CLOCKEVENT)
        !           518:        msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
        !           519:                up->unit, pp->a_lastcode);
        !           520:     }
        !           521: 
        !           522:   /*
        !           523:    * If NeoClock4X is not synchronized to a radio clock
        !           524:    * check if we're allowed to synchronize with the quartz
        !           525:    * clock.
        !           526:    */
        !           527:   up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
        !           528:   if(0==(pp->sloppyclockflag & CLK_FLAG2))
        !           529:     {
        !           530:       if('A' != up->timesource)
        !           531:        {
        !           532:          /* not allowed to sync with quartz clock */
        !           533:          if(0==(pp->sloppyclockflag & CLK_FLAG1))
        !           534:            {
        !           535:              refclock_report(peer, CEVNT_BADTIME);
        !           536:              pp->leap = LEAP_NOTINSYNC;
        !           537:              return;
        !           538:            }
        !           539:        }
        !           540:     }
        !           541: 
        !           542:   /* this should only used when first install is done */
        !           543:   if(pp->sloppyclockflag & CLK_FLAG4)
        !           544:     {
        !           545:       msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
        !           546:              up->unit, pp->a_lastcode);
        !           547:     }
        !           548: 
        !           549:   /* 123456789012345678901234567890123456789012345 */
        !           550:   /* S/N123456DCF1004021010001202ASX1213CR\r\n */
        !           551: 
        !           552:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
        !           553:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
        !           554:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
        !           555:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
        !           556:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
        !           557:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
        !           558:   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
        !           559: #if defined(NTP_PRE_420)
        !           560:   pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
        !           561: #else
        !           562:   pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
        !           563: #endif
        !           564: 
        !           565:   memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
        !           566:   up->radiosignal[3] = 0;
        !           567:   memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
        !           568:   up->serial[6] = 0;
        !           569:   up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
        !           570:   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
        !           571:   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
        !           572: 
        !           573:   /*
        !           574:     Validate received values at least enough to prevent internal
        !           575:     array-bounds problems, etc.
        !           576:   */
        !           577:   if((pp->hour < 0) || (pp->hour > 23) ||
        !           578:      (pp->minute < 0) || (pp->minute > 59) ||
        !           579:      (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
        !           580:      (day < 1) || (day > 31) ||
        !           581:      (month < 1) || (month > 12) ||
        !           582:      (pp->year < 0) || (pp->year > 99)) {
        !           583:     /* Data out of range. */
        !           584:     NLOG(NLOG_CLOCKEVENT)
        !           585:       msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
        !           586:              up->unit, pp->a_lastcode);
        !           587:     refclock_report(peer, CEVNT_BADDATE);
        !           588:     return;
        !           589:   }
        !           590: 
        !           591:   /* Year-2000 check not needed anymore. Same problem
        !           592:    * will arise at 2099 but what should we do...?
        !           593:    *
        !           594:    * wrap 2-digit date into 4-digit
        !           595:    *
        !           596:    * if(pp->year < YEAR_PIVOT)
        !           597:    * {
        !           598:    *   pp->year += 100;
        !           599:    * }
        !           600:   */
        !           601:   pp->year += 2000;
        !           602: 
        !           603:   /* adjust NeoClock4X local time to UTC */
        !           604:   calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
        !           605:   calc_utc -= 3600;
        !           606:   /* adjust NeoClock4X daylight saving time if needed */
        !           607:   if('S' == up->dststatus)
        !           608:     calc_utc -= 3600;
        !           609:   neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
        !           610: 
        !           611:   /*
        !           612:     some preparations
        !           613:   */
        !           614:   pp->day = ymd2yd(pp->year, month, day);
        !           615:   pp->leap = 0;
        !           616: 
        !           617:   if(pp->sloppyclockflag & CLK_FLAG4)
        !           618:     {
        !           619:       msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
        !           620:              up->unit,
        !           621:              pp->year, month, day,
        !           622:              pp->hour, pp->minute, pp->second,
        !           623: #if defined(NTP_PRE_420)
        !           624:               pp->msec
        !           625: #else
        !           626:               pp->nsec/NSEC_TO_MILLI
        !           627: #endif
        !           628:               );
        !           629:     }
        !           630: 
        !           631:   up->utc_year   = pp->year;
        !           632:   up->utc_month  = month;
        !           633:   up->utc_day    = day;
        !           634:   up->utc_hour   = pp->hour;
        !           635:   up->utc_minute = pp->minute;
        !           636:   up->utc_second = pp->second;
        !           637: #if defined(NTP_PRE_420)
        !           638:   up->utc_msec   = pp->msec;
        !           639: #else
        !           640:   up->utc_msec   = pp->nsec/NSEC_TO_MILLI;
        !           641: #endif
        !           642: 
        !           643:   if(!refclock_process(pp))
        !           644:     {
        !           645:       NLOG(NLOG_CLOCKEVENT)
        !           646:        msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
        !           647:       refclock_report(peer, CEVNT_FAULT);
        !           648:       return;
        !           649:     }
        !           650:   refclock_receive(peer);
        !           651: 
        !           652:   /* report good status */
        !           653:   refclock_report(peer, CEVNT_NOMINAL);
        !           654: 
        !           655:   record_clock_stats(&peer->srcadr, pp->a_lastcode);
        !           656: }
        !           657: 
        !           658: static void
        !           659: neoclock4x_poll(int unit,
        !           660:                struct peer *peer)
        !           661: {
        !           662:   struct neoclock4x_unit *up;
        !           663:   struct refclockproc *pp;
        !           664: 
        !           665:   pp = peer->procptr;
        !           666:   up = (struct neoclock4x_unit *)pp->unitptr;
        !           667: 
        !           668:   pp->polls++;
        !           669:   up->recvnow = 1;
        !           670: }
        !           671: 
        !           672: static void
        !           673: neoclock4x_control(int unit,
        !           674:                   struct refclockstat *in,
        !           675:                   struct refclockstat *out,
        !           676:                   struct peer *peer)
        !           677: {
        !           678:   struct neoclock4x_unit *up;
        !           679:   struct refclockproc *pp;
        !           680: 
        !           681:   if(NULL == peer)
        !           682:     {
        !           683:       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
        !           684:       return;
        !           685:     }
        !           686: 
        !           687:   pp = peer->procptr;
        !           688:   if(NULL == pp)
        !           689:     {
        !           690:       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
        !           691:       return;
        !           692:     }
        !           693: 
        !           694:   up = (struct neoclock4x_unit *)pp->unitptr;
        !           695:   if(NULL == up)
        !           696:     {
        !           697:       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
        !           698:       return;
        !           699:     }
        !           700: 
        !           701:   if(NULL != in)
        !           702:     {
        !           703:       /* check to see if a user supplied time offset is given */
        !           704:       if(in->haveflags & CLK_HAVETIME1)
        !           705:        {
        !           706:          pp->fudgetime1 = in->fudgetime1;
        !           707:          NLOG(NLOG_CLOCKINFO)
        !           708:            msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
        !           709:                    unit, pp->fudgetime1);
        !           710:        }
        !           711: 
        !           712:       /* notify */
        !           713:       if(pp->sloppyclockflag & CLK_FLAG1)
        !           714:        {
        !           715:          NLOG(NLOG_CLOCKINFO)
        !           716:            msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
        !           717:        }
        !           718:       else
        !           719:        {
        !           720:          NLOG(NLOG_CLOCKINFO)
        !           721:            msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
        !           722:        }
        !           723:     }
        !           724: 
        !           725:   if(NULL != out)
        !           726:     {
        !           727:       char *tt;
        !           728:       char tmpbuf[80];
        !           729: 
        !           730:       out->kv_list = (struct ctl_var *)0;
        !           731:       out->type    = REFCLK_NEOCLOCK4X;
        !           732: 
        !           733:       snprintf(tmpbuf, sizeof(tmpbuf)-1,
        !           734:               "%04d-%02d-%02d %02d:%02d:%02d.%03d",
        !           735:               up->utc_year, up->utc_month, up->utc_day,
        !           736:               up->utc_hour, up->utc_minute, up->utc_second,
        !           737:               up->utc_msec);
        !           738:       tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
        !           739:       snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
        !           740: 
        !           741:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           742:       snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
        !           743:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           744:       snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
        !           745:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           746:       snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
        !           747:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           748:       if('A' == up->timesource)
        !           749:        snprintf(tt, 39, "timesource=\"radio\"");
        !           750:       else if('C' == up->timesource)
        !           751:        snprintf(tt, 39, "timesource=\"quartz\"");
        !           752:       else
        !           753:        snprintf(tt, 39, "timesource=\"unknown\"");
        !           754:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           755:       if('I' == up->quarzstatus)
        !           756:        snprintf(tt, 39, "quartzstatus=\"synchronized\"");
        !           757:       else if('X' == up->quarzstatus)
        !           758:         snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
        !           759:       else
        !           760:        snprintf(tt, 39, "quartzstatus=\"unknown\"");
        !           761:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           762:       if('S' == up->dststatus)
        !           763:         snprintf(tt, 39, "dststatus=\"summer\"");
        !           764:       else if('W' == up->dststatus)
        !           765:         snprintf(tt, 39, "dststatus=\"winter\"");
        !           766:       else
        !           767:         snprintf(tt, 39, "dststatus=\"unknown\"");
        !           768:       tt = add_var(&out->kv_list, 80, RO|DEF);
        !           769:       snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
        !           770:       tt = add_var(&out->kv_list, 40, RO|DEF);
        !           771:       snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
        !           772:       tt = add_var(&out->kv_list, 80, RO|DEF);
        !           773:       snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
        !           774:       tt = add_var(&out->kv_list, 80, RO|DEF);
        !           775:       snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
        !           776:     }
        !           777: }
        !           778: 
        !           779: static int
        !           780: neol_hexatoi_len(const char str[],
        !           781:                 int *result,
        !           782:                 int maxlen)
        !           783: {
        !           784:   int hexdigit;
        !           785:   int i;
        !           786:   int n = 0;
        !           787: 
        !           788:   for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
        !           789:     {
        !           790:       hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
        !           791:       n = 16 * n + hexdigit;
        !           792:     }
        !           793:   *result = n;
        !           794:   return (n);
        !           795: }
        !           796: 
        !           797: static int
        !           798: neol_atoi_len(const char str[],
        !           799:                  int *result,
        !           800:                  int maxlen)
        !           801: {
        !           802:   int digit;
        !           803:   int i;
        !           804:   int n = 0;
        !           805: 
        !           806:   for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
        !           807:     {
        !           808:       digit = str[i] - '0';
        !           809:       n = 10 * n + digit;
        !           810:     }
        !           811:   *result = n;
        !           812:   return (n);
        !           813: }
        !           814: 
        !           815: /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
        !           816:  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
        !           817:  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
        !           818:  *
        !           819:  * [For the Julian calendar (which was used in Russia before 1917,
        !           820:  * Britain & colonies before 1752, anywhere else before 1582,
        !           821:  * and is still in use by some communities) leave out the
        !           822:  * -year/100+year/400 terms, and add 10.]
        !           823:  *
        !           824:  * This algorithm was first published by Gauss (I think).
        !           825:  *
        !           826:  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
        !           827:  * machines were long is 32-bit! (However, as time_t is signed, we
        !           828:  * will already get problems at other places on 2038-01-19 03:14:08)
        !           829:  */
        !           830: static unsigned long
        !           831: neol_mktime(int year,
        !           832:            int mon,
        !           833:            int day,
        !           834:            int hour,
        !           835:            int min,
        !           836:            int sec)
        !           837: {
        !           838:   if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
        !           839:     mon += 12;      /* Puts Feb last since it has leap day */
        !           840:     year -= 1;
        !           841:   }
        !           842:   return (((
        !           843:             (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
        !           844:             year*365 - 719499
        !           845:             )*24 + hour /* now have hours */
        !           846:            )*60 + min /* now have minutes */
        !           847:           )*60 + sec; /* finally seconds */
        !           848: }
        !           849: 
        !           850: static void
        !           851: neol_localtime(unsigned long utc,
        !           852:               int* year,
        !           853:               int* month,
        !           854:               int* day,
        !           855:               int* hour,
        !           856:               int* min,
        !           857:               int* sec)
        !           858: {
        !           859:   *sec = utc % 60;
        !           860:   utc /= 60;
        !           861:   *min = utc % 60;
        !           862:   utc /= 60;
        !           863:   *hour = utc % 24;
        !           864:   utc /= 24;
        !           865: 
        !           866:   /*             JDN Date 1/1/1970 */
        !           867:   neol_jdn_to_ymd(utc + 2440588L, year, month, day);
        !           868: }
        !           869: 
        !           870: static void
        !           871: neol_jdn_to_ymd(unsigned long jdn,
        !           872:                int *yy,
        !           873:                int *mm,
        !           874:                int *dd)
        !           875: {
        !           876:   unsigned long x, z, m, d, y;
        !           877:   unsigned long daysPer400Years = 146097UL;
        !           878:   unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
        !           879: 
        !           880:   x = jdn + 68569UL;
        !           881:   z = 4UL * x / daysPer400Years;
        !           882:   x = x - (daysPer400Years * z + 3UL) / 4UL;
        !           883:   y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
        !           884:   x = x - 1461UL * y / 4UL + 31UL;
        !           885:   m = 80UL * x / 2447UL;
        !           886:   d = x - 2447UL * m / 80UL;
        !           887:   x = m / 11UL;
        !           888:   m = m + 2UL - 12UL * x;
        !           889:   y = 100UL * (z - 49UL) + y + x;
        !           890: 
        !           891:   *yy = (int)y;
        !           892:   *mm = (int)m;
        !           893:   *dd = (int)d;
        !           894: }
        !           895: 
        !           896: #if !defined(NEOCLOCK4X_FIRMWARE)
        !           897: static int
        !           898: neol_query_firmware(int fd,
        !           899:                    int unit,
        !           900:                    char *firmware,
        !           901:                    int maxlen)
        !           902: {
        !           903:   char tmpbuf[256];
        !           904:   int len;
        !           905:   int lastsearch;
        !           906:   unsigned char c;
        !           907:   int last_c_was_crlf;
        !           908:   int last_crlf_conv_len;
        !           909:   int init;
        !           910:   int read_errors;
        !           911:   int flag = 0;
        !           912:   int chars_read;
        !           913: 
        !           914:   /* wait a little bit */
        !           915:   sleep(1);
        !           916:   if(-1 != write(fd, "V", 1))
        !           917:     {
        !           918:       /* wait a little bit */
        !           919:       sleep(1);
        !           920:       memset(tmpbuf, 0x00, sizeof(tmpbuf));
        !           921: 
        !           922:       len = 0;
        !           923:       lastsearch = 0;
        !           924:       last_c_was_crlf = 0;
        !           925:       last_crlf_conv_len = 0;
        !           926:       init = 1;
        !           927:       read_errors = 0;
        !           928:       chars_read = 0;
        !           929:       for(;;)
        !           930:        {
        !           931:          if(read_errors > 5)
        !           932:            {
        !           933:              msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
        !           934:              strcpy(tmpbuf, "unknown due to timeout");
        !           935:              break;
        !           936:            }
        !           937:           if(chars_read > 500)
        !           938:             {
        !           939:              msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
        !           940:              strcpy(tmpbuf, "unknown due to garbage input");
        !           941:              break;
        !           942:             }
        !           943:          if(-1 == read(fd, &c, 1))
        !           944:            {
        !           945:               if(EAGAIN != errno)
        !           946:                 {
        !           947:                   msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
        !           948:                   read_errors++;
        !           949:                 }
        !           950:               else
        !           951:                 {
        !           952:                   sleep(1);
        !           953:                 }
        !           954:              continue;
        !           955:            }
        !           956:           else
        !           957:             {
        !           958:               chars_read++;
        !           959:             }
        !           960: 
        !           961:          if(init)
        !           962:            {
        !           963:              if(0xA9 != c) /* wait for (c) char in input stream */
        !           964:                continue;
        !           965: 
        !           966:              strcpy(tmpbuf, "(c)");
        !           967:              len = 3;
        !           968:              init = 0;
        !           969:              continue;
        !           970:            }
        !           971: 
        !           972: #if 0
        !           973:          msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
        !           974: #endif
        !           975: 
        !           976:          if(0x0A == c || 0x0D == c)
        !           977:            {
        !           978:              if(last_c_was_crlf)
        !           979:                {
        !           980:                  char *ptr;
        !           981:                  ptr = strstr(&tmpbuf[lastsearch], "S/N");
        !           982:                  if(NULL != ptr)
        !           983:                    {
        !           984:                      tmpbuf[last_crlf_conv_len] = 0;
        !           985:                      flag = 1;
        !           986:                      break;
        !           987:                    }
        !           988:                  /* convert \n to / */
        !           989:                  last_crlf_conv_len = len;
        !           990:                  tmpbuf[len++] = ' ';
        !           991:                  tmpbuf[len++] = '/';
        !           992:                  tmpbuf[len++] = ' ';
        !           993:                  lastsearch = len;
        !           994:                }
        !           995:              last_c_was_crlf = 1;
        !           996:            }
        !           997:          else
        !           998:            {
        !           999:              last_c_was_crlf = 0;
        !          1000:              if(0x00 != c)
        !          1001:                tmpbuf[len++] = (char) c;
        !          1002:            }
        !          1003:          tmpbuf[len] = '\0';
        !          1004:          if(len > sizeof(tmpbuf)-5)
        !          1005:            break;
        !          1006:        }
        !          1007:     }
        !          1008:   else
        !          1009:     {
        !          1010:       msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
        !          1011:       strcpy(tmpbuf, "unknown error");
        !          1012:     }
        !          1013:   strncpy(firmware, tmpbuf, maxlen);
        !          1014:   firmware[maxlen] = '\0';
        !          1015: 
        !          1016:   if(flag)
        !          1017:     {
        !          1018:       NLOG(NLOG_CLOCKINFO)
        !          1019:        msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
        !          1020: 
        !          1021:       if(strstr(firmware, "/R2"))
        !          1022:        {
        !          1023:          msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
        !          1024:        }
        !          1025: 
        !          1026:     }
        !          1027: 
        !          1028:   return (flag);
        !          1029: }
        !          1030: 
        !          1031: static int
        !          1032: neol_check_firmware(int unit,
        !          1033:                     const char *firmware,
        !          1034:                     char *firmwaretag)
        !          1035: {
        !          1036:   char *ptr;
        !          1037: 
        !          1038:   *firmwaretag = '?';
        !          1039:   ptr = strstr(firmware, "NDF:");
        !          1040:   if(NULL != ptr)
        !          1041:     {
        !          1042:       if((strlen(firmware) - strlen(ptr)) >= 7)
        !          1043:         {
        !          1044:           if(':' == *(ptr+5) && '*' == *(ptr+6))
        !          1045:             *firmwaretag = *(ptr+4);
        !          1046:         }
        !          1047:     }
        !          1048: 
        !          1049:   if('A' != *firmwaretag)
        !          1050:     {
        !          1051:       msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
        !          1052:       return (0);
        !          1053:     }
        !          1054: 
        !          1055:   return (1);
        !          1056: }
        !          1057: #endif
        !          1058: 
        !          1059: #else
        !          1060: int refclock_neoclock4x_bs;
        !          1061: #endif /* REFCLOCK */
        !          1062: 
        !          1063: /*
        !          1064:  * History:
        !          1065:  * refclock_neoclock4x.c
        !          1066:  *
        !          1067:  * 2002/04/27 cjh
        !          1068:  * Revision 1.0  first release
        !          1069:  *
        !          1070:  * 2002/07/15 cjh
        !          1071:  * preparing for bitkeeper reposity
        !          1072:  *
        !          1073:  * 2002/09/09 cjh
        !          1074:  * Revision 1.1
        !          1075:  * - don't assume sprintf returns an int anymore
        !          1076:  * - change the way the firmware version is read
        !          1077:  * - some customers would like to put a device called
        !          1078:  *   data diode to the NeoClock4X device to disable
        !          1079:  *   the write line. We need to now the firmware
        !          1080:  *   version even in this case. We made a compile time
        !          1081:  *   definition in this case. The code was previously
        !          1082:  *   only available on request.
        !          1083:  *
        !          1084:  * 2003/01/08 cjh
        !          1085:  * Revision 1.11
        !          1086:  * - changing xprinf to xnprinf to avoid buffer overflows
        !          1087:  * - change some logic
        !          1088:  * - fixed memory leaks if drivers can't initialize
        !          1089:  *
        !          1090:  * 2003/01/10 cjh
        !          1091:  * Revision 1.12
        !          1092:  * - replaced ldiv
        !          1093:  * - add code to support FreeBSD
        !          1094:  *
        !          1095:  * 2003/07/07 cjh
        !          1096:  * Revision 1.13
        !          1097:  * - fix reporting of clock status
        !          1098:  *   changes. previously a bad clock
        !          1099:  *   status was never reset.
        !          1100:  *
        !          1101:  * 2004/04/07 cjh
        !          1102:  * Revision 1.14
        !          1103:  * - open serial port in a way
        !          1104:  *   AIX and some other OS can
        !          1105:  *   handle much better
        !          1106:  *
        !          1107:  * 2006/01/11 cjh
        !          1108:  * Revision 1.15
        !          1109:  * - remove some unsued #ifdefs
        !          1110:  * - fix nsec calculation, closes #499
        !          1111:  *
        !          1112:  * 2009/12/04 cjh
        !          1113:  * Revision 1.16
        !          1114:  * - change license to ntp COPYRIGHT notice. This should allow Debian
        !          1115:  *   to add this refclock driver in further releases.
        !          1116:  * - detect R2 hardware
        !          1117:  *
        !          1118:  */
        !          1119: 
        !          1120: 
        !          1121: 
        !          1122: 
        !          1123: 
        !          1124: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>