File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_neoclock4x.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 1 month ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    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>