/*      $NetBSD: refclock_jjy.c,v 1.17 2024/08/18 20:47:18 christos Exp $       */

/*
* refclock_jjy - clock driver for JJY receivers
*/

/**********************************************************************/
/*                                                                    */
/*  Copyright (C) 2001-2020, Takao Abe.  All rights reserved.         */
/*                                                                    */
/*  Permission to use, copy, modify, and distribute this software     */
/*  and its documentation for any purpose is hereby granted           */
/*  without fee, provided that the following conditions are met:      */
/*                                                                    */
/*  One retains the entire copyright notice properly, and both the    */
/*  copyright notice and this license. in the documentation and/or    */
/*  other materials provided with the distribution.                   */
/*                                                                    */
/*  This software and the name of the author must not be used to      */
/*  endorse or promote products derived from this software without    */
/*  prior written permission.                                         */
/*                                                                    */
/*  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED    */
/*  WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE        */
/*  IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A          */
/*  PARTICULAR PURPOSE.                                               */
/*  IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT,  */
/*  INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   */
/*  ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE        */
/*  GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS      */
/*  INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,     */
/*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING        */
/*  NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF    */
/*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/*                                                                    */
/*  This driver is developed in my private time, and is opened as     */
/*  voluntary contributions for the NTP.                              */
/*  The manufacturer of the JJY receiver has not participated in      */
/*  a development of this driver.                                     */
/*  The manufacturer does not warrant anything about this driver,     */
/*  and is not liable for anything about this driver.                 */
/*                                                                    */
/**********************************************************************/
/*                                                                    */
/*  Author     Takao Abe                                              */
/*  Email      [email protected]                                      */
/*  Homepage   http://www.bea.hi-ho.ne.jp/abetakao/                   */
/*                                                                    */
/*  The email address [email protected] is never read          */
/*  from 2010, because a few filtering rule are provided by the       */
/*  "hi-ho.ne.jp", and lots of spam mail are reached.                 */
/*  New email address for supporting the refclock_jjy is              */
/*  [email protected]                                                 */
/*                                                                    */
/**********************************************************************/
/*                                                                    */
/*  History                                                           */
/*                                                                    */
/*  2001/07/15                                                        */
/*    [New]    Support the Tristate Ltd. JJY receiver                 */
/*                                                                    */
/*  2001/08/04                                                        */
/*    [Change] Log to clockstats even if bad reply                    */
/*    [Fix]    PRECISION = (-3) (about 100 ms)                        */
/*    [Add]    Support the C-DEX Co.Ltd. JJY receiver                 */
/*                                                                    */
/*  2001/12/04                                                        */
/*    [Fix]    C-DEX JST2000 ( [email protected] )      */
/*                                                                    */
/*  2002/07/12                                                        */
/*    [Fix]    Portability for FreeBSD ( patched by the user )        */
/*                                                                    */
/*  2004/10/31                                                        */
/*    [Change] Command send timing for the Tristate Ltd. JJY receiver */
/*             JJY-01 ( Firmware version 2.01 )                       */
/*             Thanks to Andy Taki for testing under FreeBSD          */
/*                                                                    */
/*  2004/11/28                                                        */
/*    [Add]    Support the Echo Keisokuki LT-2000 receiver            */
/*                                                                    */
/*  2006/11/04                                                        */
/*    [Fix]    C-DEX JST2000                                          */
/*             Thanks to Hideo Kuramatsu for the patch                */
/*                                                                    */
/*  2009/04/05                                                        */
/*    [Add]    Support the CITIZEN T.I.C JJY-200 receiver             */
/*                                                                    */
/*  2010/11/20                                                        */
/*    [Change] Bug 1618 ( Harmless )                                  */
/*             Code clean up ( Remove unreachable codes ) in          */
/*             jjy_start()                                            */
/*    [Change] Change clockstats format of the Tristate JJY01/02      */
/*             Issues more command to get the status of the receiver  */
/*             when "fudge 127.127.40.X flag1 1" is specified         */
/*             ( DATE,STIM -> DCST,STUS,DATE,STIM )                   */
/*                                                                    */
/*  2011/04/30                                                        */
/*    [Add]    Support the Tristate Ltd. TS-GPSclock-01               */
/*                                                                    */
/*  2015/03/29                                                        */
/*    [Add]    Support the Telephone JJY                              */
/*    [Change] Split the start up routine into each JJY receivers.    */
/*             Change raw data internal bufferring process            */
/*             Change over midnight handling of TS-JJY01 and TS-GPS01 */
/*             to put DATE command between before and after TIME's.   */
/*             Unify the writing clockstats of all JJY receivers.     */
/*                                                                    */
/*  2015/05/15                                                        */
/*    [Add]    Support the SEIKO TIME SYSTEMS TDC-300                 */
/*                                                                    */
/*  2016/05/08                                                        */
/*    [Fix]    C-DEX JST2000                                          */
/*             Thanks to Mr. Kuramatsu for the report and the patch.  */
/*                                                                    */
/*  2017/04/30                                                        */
/*    [Change] Avoid a wrong report of the coverity static analysis   */
/*             tool. ( The code is harmless and has no bug. )         */
/*             teljjy_conn_send()                                     */
/*                                                                    */
/*  2020/01/19                                                        */
/*    [Change] Handling TS-JJY01/02 status of the the STUS reply.     */
/*             Time synchronization can be skipped by the settings of */
/*             the flag2 when the status of the reply is UNADJUSTED.  */
/*    [Change] Quiet compilation for the GCC 9.2.0.                   */
/*    [Fix]    Correct typos in comment lines                         */
/*                                                                    */
/**********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(REFCLOCK) && defined(CLOCK_JJY)

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_tty.h"
#include "ntp_refclock.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"

/**********************************************************************/

/*
* Interface definitions
*/
#define DEVICE          "/dev/jjy%d"    /* device name and unit */
#define SPEED232_TRISTATE_JJY01         B9600   /* UART speed (9600 baud) */
#define SPEED232_CDEX_JST2000           B9600   /* UART speed (9600 baud) */
#define SPEED232_ECHOKEISOKUKI_LT2000   B9600   /* UART speed (9600 baud) */
#define SPEED232_CITIZENTIC_JJY200      B4800   /* UART speed (4800 baud) */
#define SPEED232_TRISTATE_GPSCLOCK01    B38400  /* USB  speed (38400 baud) */
#define SPEED232_SEIKO_TIMESYS_TDC_300  B2400   /* UART speed (2400 baud) */
#define SPEED232_TELEPHONE              B2400   /* UART speed (4800 baud) */
#define REFID           "JJY"           /* reference ID */
#define DESCRIPTION     "JJY Receiver"
#define PRECISION       (-3)            /* precision assumed (about 100 ms) */

/*
* JJY unit control structure
*/

struct jjyRawDataBreak {
       const char *    pString ;
       int             iLength ;
} ;

#define MAX_TIMESTAMP   6
#define MAX_RAWBUF      100
#define MAX_LOOPBACK    5

struct jjyunit {
/* Set up by the function "jjy_start_xxxxxxxx" */
       char    unittype ;          /* UNITTYPE_XXXXXXXXXX */
       short   operationmode ;     /* Echo Keisokuki LT-2000 */
       int     linespeed ;         /* SPEED232_XXXXXXXXXX */
       short   linediscipline ;    /* LDISC_CLK or LDISC_RAW */
/* Receiving data */
       char    bInitError ;        /* Set by jjy_start if any error during initialization */
       short   iProcessState ;     /* JJY_PROCESS_STATE_XXXXXX */
       char    bReceiveFlag ;      /* Set and reset by jjy_receive */
       char    bLineError ;        /* Reset by jjy_poll / Set by jjy_receive_xxxxxxxx*/
       short   iCommandSeq ;       /* 0:Idle  Non-Zero:Issued */
       short   iReceiveSeq ;
       int     iLineCount ;
       int     year, month, day, hour, minute, second, msecond ;
       int     leapsecond ;
       int     iTimestampCount ;   /* TS-JJY01, TS-GPS01, Telephone-JJY */
       int     iTimestamp [ MAX_TIMESTAMP ] ;  /* Serial second ( 0 - 86399 ) */
/* LDISC_RAW only */
       char    sRawBuf [ MAX_RAWBUF ] ;
       int     iRawBufLen ;
       struct  jjyRawDataBreak *pRawBreak ;
       char    bWaitBreakString ;
       char    sLineBuf [ MAX_RAWBUF ] ;
       int     iLineBufLen ;
       char    sTextBuf [ MAX_RAWBUF ] ;
       int     iTextBufLen ;
       char    bSkipCntrlCharOnly ;
/* TS-JJY01, TS-JJY02 */
       time_t  tLastAdjustedTimestamp ;
       char    bStusReplyAdjusted ;
       char    bStusReplyAdjustedAtLeastOnce ;
/* Telephone JJY auto measurement of the loopback delay */
       char    bLoopbackMode ;
       short   iLoopbackCount ;
       struct  timeval sendTime[MAX_LOOPBACK], delayTime[MAX_LOOPBACK] ;
       char    bLoopbackTimeout[MAX_LOOPBACK] ;
       short   iLoopbackValidCount ;
/* Telephone JJY timer */
       short   iTeljjySilentTimer ;
       short   iTeljjyStateTimer ;
/* Telephone JJY control finite state machine */
       short   iClockState ;
       short   iClockEvent ;
       short   iClockCommandSeq ;
/* Modem timer */
       short   iModemSilentCount ;
       short   iModemSilentTimer ;
       short   iModemStateTimer ;
/* Modem control finite state machine */
       short   iModemState ;
       short   iModemEvent ;
       short   iModemCommandSeq ;
};

#define UNITTYPE_TRISTATE_JJY01         1
#define UNITTYPE_CDEX_JST2000           2
#define UNITTYPE_ECHOKEISOKUKI_LT2000   3
#define UNITTYPE_CITIZENTIC_JJY200      4
#define UNITTYPE_TRISTATE_GPSCLOCK01    5
#define UNITTYPE_SEIKO_TIMESYS_TDC_300  6
#define UNITTYPE_TELEPHONE              100

#define JJY_PROCESS_STATE_IDLE          0
#define JJY_PROCESS_STATE_POLL          1
#define JJY_PROCESS_STATE_RECEIVE       2
#define JJY_PROCESS_STATE_DONE          3
#define JJY_PROCESS_STATE_ERROR         4

/**********************************************************************/

/*
*  Function calling structure
*
*  jjy_start
*   |--  jjy_start_tristate_jjy01
*   |--  jjy_start_cdex_jst2000
*   |--  jjy_start_echokeisokuki_lt2000
*   |--  jjy_start_citizentic_jjy200
*   |--  jjy_start_tristate_gpsclock01
*   |--  jjy_start_seiko_tsys_tdc_300
*   |--  jjy_start_telephone
*
*  jjy_shutdown
*
*  jjy_poll
*   |--  jjy_poll_tristate_jjy01
*   |--  jjy_poll_cdex_jst2000
*   |--  jjy_poll_echokeisokuki_lt2000
*   |--  jjy_poll_citizentic_jjy200
*   |--  jjy_poll_tristate_gpsclock01
*   |--  jjy_poll_seiko_tsys_tdc_300
*   |--  jjy_poll_telephone
*         |--  teljjy_control
*               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*                     |--  modem_connect
*                           |--  modem_control
*                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*
*  jjy_receive
*   |
*   |--  jjy_receive_tristate_jjy01
*   |     |--  jjy_synctime
*   |--  jjy_receive_cdex_jst2000
*   |     |--  jjy_synctime
*   |--  jjy_receive_echokeisokuki_lt2000
*   |     |--  jjy_synctime
*   |--  jjy_receive_citizentic_jjy200
*   |     |--  jjy_synctime
*   |--  jjy_receive_tristate_gpsclock01
*   |     |--  jjy_synctime
*   |--  jjy_receive_seiko_tsys_tdc_300
*   |     |--  jjy_synctime
*   |--  jjy_receive_telephone
*         |--  modem_receive
*         |     |--  modem_control
*         |           |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*         |--  teljjy_control
*               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*                     |--  jjy_synctime
*                     |--  modem_disconnect
*                           |--  modem_control
*                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*
*  jjy_timer
*   |--  jjy_timer_telephone
*         |--  modem_timer
*         |     |--  modem_control
*         |           |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*         |--  teljjy_control
*               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*                     |--  modem_disconnect
*                           |--  modem_control
*                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
*
* Function prototypes
*/

static  int     jjy_start                       (int, struct peer *);
static  int     jjy_start_tristate_jjy01        (int, struct peer *, struct jjyunit *);
static  int     jjy_start_cdex_jst2000          (int, struct peer *, struct jjyunit *);
static  int     jjy_start_echokeisokuki_lt2000  (int, struct peer *, struct jjyunit *);
static  int     jjy_start_citizentic_jjy200     (int, struct peer *, struct jjyunit *);
static  int     jjy_start_tristate_gpsclock01   (int, struct peer *, struct jjyunit *);
static  int     jjy_start_seiko_tsys_tdc_300    (int, struct peer *, struct jjyunit *);
static  int     jjy_start_telephone             (int, struct peer *, struct jjyunit *);

static  void    jjy_shutdown                    (int, struct peer *);

static  void    jjy_poll                        (int, struct peer *);
static  void    jjy_poll_tristate_jjy01         (int, struct peer *);
static  void    jjy_poll_cdex_jst2000           (int, struct peer *);
static  void    jjy_poll_echokeisokuki_lt2000   (int, struct peer *);
static  void    jjy_poll_citizentic_jjy200      (int, struct peer *);
static  void    jjy_poll_tristate_gpsclock01    (int, struct peer *);
static  void    jjy_poll_seiko_tsys_tdc_300     (int, struct peer *);
static  void    jjy_poll_telephone              (int, struct peer *);

static  void    jjy_receive                     (struct recvbuf *);
static  int     jjy_receive_tristate_jjy01      (struct recvbuf *);
static  int     jjy_receive_cdex_jst2000        (struct recvbuf *);
static  int     jjy_receive_echokeisokuki_lt2000 (struct recvbuf *);
static  int     jjy_receive_citizentic_jjy200   (struct recvbuf *);
static  int     jjy_receive_tristate_gpsclock01 (struct recvbuf *);
static  int     jjy_receive_seiko_tsys_tdc_300  (struct recvbuf *);
static  int     jjy_receive_telephone           (struct recvbuf *);

static  void    jjy_timer                       (int, struct peer *);
static  void    jjy_timer_telephone             (int, struct peer *);

static  void    jjy_synctime                    ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  void    jjy_write_clockstats            ( struct peer *, int, const char* ) ;

static  int     getRawDataBreakPosition         ( struct jjyunit *, int ) ;

static  short   getModemState                   ( struct jjyunit * ) ;
static  int     isModemStateConnect             ( short ) ;
static  int     isModemStateDisconnect          ( short ) ;
static  int     isModemStateTimerOn             ( struct jjyunit * ) ;
static  void    modem_connect                   ( int, struct peer * ) ;
static  void    modem_disconnect                ( int, struct peer * ) ;
static  int     modem_receive                   ( struct recvbuf * ) ;
static  void    modem_timer                     ( int, struct peer * );

static  void    printableString ( char*, int, const char*, int ) ;

/*
* Transfer vector
*/
struct  refclock refclock_jjy = {
       jjy_start,      /* start up driver */
       jjy_shutdown,   /* shutdown driver */
       jjy_poll,       /* transmit poll message */
       noentry,        /* not used */
       noentry,        /* not used */
       noentry,        /* not used */
       jjy_timer       /* 1 second interval timer */
};

/*
* Start up driver return code
*/
#define RC_START_SUCCESS        1
#define RC_START_ERROR          0

/*
* Local constants definition
*/

#define MAX_LOGTEXT     200

#ifndef TRUE
#define TRUE    (0==0)
#endif
#ifndef FALSE
#define FALSE   (!TRUE)
#endif

/* Local constants definition for the return code of the jjy_receive_xxxxxxxx */

#define JJY_RECEIVE_DONE        0
#define JJY_RECEIVE_SKIP        1
#define JJY_RECEIVE_UNPROCESS   2
#define JJY_RECEIVE_WAIT        3
#define JJY_RECEIVE_ERROR       4

/* Local constants definition for the 2nd parameter of the jjy_write_clockstats */

#define JJY_CLOCKSTATS_MARK_NONE        0
#define JJY_CLOCKSTATS_MARK_JJY         1
#define JJY_CLOCKSTATS_MARK_SEND        2
#define JJY_CLOCKSTATS_MARK_RECEIVE     3
#define JJY_CLOCKSTATS_MARK_INFORMATION 4
#define JJY_CLOCKSTATS_MARK_ATTENTION   5
#define JJY_CLOCKSTATS_MARK_WARNING     6
#define JJY_CLOCKSTATS_MARK_ERROR       7
#define JJY_CLOCKSTATS_MARK_BUG         8

/* Local constants definition for the clockstats messages */

#define JJY_CLOCKSTATS_MESSAGE_ECHOBACK                 "* Echoback"
#define JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY             "* Ignore replay : [%s]"
#define JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2          "* Over midnight : timestamp=%d, %d"
#define JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_3          "* Over midnight : timestamp=%d, %d, %d"
#define JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE         "* Unsure timestamp : %s"
#define JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY           "* Loopback delay : %d.%03d mSec."
#define JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST             "* Delay adjustment : %d mSec. ( valid=%hd/%d )"
#define JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST           "* Delay adjustment : None ( valid=%hd/%d )"
#define JJY_CLOCKSTATS_MESSAGE_STUS_UNADJUSTED          "* Skip time synchronization : STUS is 'UNADJUSTED' for %.0lf %s"

#define JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY         "# Unexpected reply : [%s]"
#define JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH           "# Invalid length : length=%d"
#define JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY           "# Too many reply : count=%d"
#define JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY            "# Invalid reply : [%s]"
#define JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2             "# Slow reply : timestamp=%d, %d"
#define JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_3             "# Slow reply : timestamp=%d, %d, %d"
#define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE      "# Invalid date : rc=%d year=%d month=%d day=%d"
#define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME      "# Invalid time : rc=%d hour=%d minute=%d second=%d"
#define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME  "# Invalid time : rc=%d year=%d month=%d day=%d hour=%d minute=%d second=%d"
#define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP      "# Invalid leap : leapsecond=[%s]"
#define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_STATUS    "# Invalid status : status=[%s]"

/* Debug print macro */

#ifdef  DEBUG
#define DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen)    { if ( debug ) { printf ( "refclock_jjy.c : %s : iProcessState=%d bLineError=%d iCommandSeq=%d iLineCount=%d iTimestampCount=%d iLen=%d\n", sFunc, up->iProcessState, up->bLineError, up->iCommandSeq, up->iLineCount, up->iTimestampCount, iLen ) ; } }
#else
#define DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen)
#endif

/**************************************************************************************************/
/*  jjy_start - open the devices and initialize data for processing                               */
/**************************************************************************************************/
static int
jjy_start ( int unit, struct peer *peer )
{

       struct  refclockproc *pp ;
       struct  jjyunit      *up ;
       int     rc ;
       int     fd ;
       char    sDeviceName [ sizeof(DEVICE) + 10 ], sLog [ 60 ] ;

#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : jjy_start : %s  mode=%d  dev=%s  unit=%d\n",
                        ntoa(&peer->srcadr), peer->ttl, DEVICE, unit ) ;
       }
#endif

       /* Allocate memory for the unit structure */
       up = emalloc( sizeof(*up) ) ;
       if ( up == NULL ) {
               msyslog ( LOG_ERR, "refclock_jjy.c : jjy_start : emalloc" ) ;
               return RC_START_ERROR ;
       }
       memset ( up, 0, sizeof(*up) ) ;

       up->bInitError = FALSE ;
       up->iProcessState = JJY_PROCESS_STATE_IDLE ;
       up->bReceiveFlag = FALSE ;
       up->iCommandSeq = 0 ;
       up->iLineCount = 0 ;
       up->iTimestampCount = 0 ;
       up->bWaitBreakString = FALSE ;
       up->iRawBufLen = up->iLineBufLen = up->iTextBufLen = 0 ;
       up->bSkipCntrlCharOnly = TRUE ;

       /* Set up the device name */
       snprintf( sDeviceName, sizeof(sDeviceName), DEVICE, unit ) ;

       snprintf( sLog, sizeof(sLog), "mode=%d dev=%s", peer->ttl, sDeviceName ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

       /*
        * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
        */
       switch ( peer->ttl ) {
       case 0 :
       case 1 :
               rc = jjy_start_tristate_jjy01 ( unit, peer, up ) ;
               break ;
       case 2 :
               rc = jjy_start_cdex_jst2000 ( unit, peer, up ) ;
               break ;
       case 3 :
               rc = jjy_start_echokeisokuki_lt2000 ( unit, peer, up ) ;
               break ;
       case 4 :
               rc = jjy_start_citizentic_jjy200 ( unit, peer, up ) ;
               break ;
       case 5 :
               rc = jjy_start_tristate_gpsclock01 ( unit, peer, up ) ;
               break ;
       case 6 :
               rc = jjy_start_seiko_tsys_tdc_300 ( unit, peer, up ) ;
               break ;
       case 100 :
               rc = jjy_start_telephone ( unit, peer, up ) ;
               break ;
       default :
               if ( 101 <= peer->ttl && peer->ttl <= 180 ) {
                       rc = jjy_start_telephone ( unit, peer, up ) ;
               } else {
                       msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
                                 ntoa(&peer->srcadr), peer->ttl ) ;
                       free ( (void*) up ) ;
               return RC_START_ERROR ;
               }
       }

       if ( rc != 0 ) {
               msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Initialize error",
                         ntoa(&peer->srcadr), peer->ttl ) ;
               free ( (void*) up ) ;
               return RC_START_ERROR ;
       }

       /* Open the device */
       fd = refclock_open ( &peer->srcadr, sDeviceName, up->linespeed, up->linediscipline ) ;
       if ( fd <= 0 ) {
               free ( (void*) up ) ;
               return RC_START_ERROR ;
       }

       /*
        * Initialize variables
        */
       pp = peer->procptr ;

       pp->clockdesc   = DESCRIPTION ;
       pp->unitptr       = up ;
       pp->io.clock_recv = jjy_receive ;
       pp->io.srcclock   = peer ;
       pp->io.datalen    = 0 ;
       pp->io.fd         = fd ;
       if ( ! io_addclock(&pp->io) ) {
               close ( fd ) ;
               pp->io.fd = -1 ;
               free ( up ) ;
               pp->unitptr = NULL ;
               return RC_START_ERROR ;
       }
       memcpy( (char*)&pp->refid, REFID, strlen(REFID) ) ;

       peer->precision = PRECISION ;

       snprintf( sLog, sizeof(sLog), "minpoll=%d maxpoll=%d", peer->minpoll, peer->maxpoll ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

       return RC_START_SUCCESS ;

}

/**************************************************************************************************/
/*  jjy_shutdown - shutdown the clock                                                             */
/**************************************************************************************************/
static void
jjy_shutdown ( int unit, struct peer *peer )
{

       struct jjyunit      *up;
       struct refclockproc *pp;

       char    sLog [ 60 ] ;

       pp = peer->procptr ;
       up = pp->unitptr ;
       if ( -1 != pp->io.fd ) {
               io_closeclock ( &pp->io ) ;
       }
       if ( NULL != up ) {
               free ( up ) ;
       }

       snprintf( sLog, sizeof(sLog), "JJY stopped. unit=%d mode=%d", unit, peer->ttl ) ;
       record_clock_stats( &peer->srcadr, sLog ) ;

}

/**************************************************************************************************/
/*  jjy_receive - receive data from the serial interface                                          */
/**************************************************************************************************/
static void
jjy_receive ( struct recvbuf *rbufp )
{
#ifdef DEBUG
       static const char *sFunctionName = "jjy_receive" ;
#endif

       struct jjyunit      *up ;
       struct refclockproc *pp ;
       struct peer         *peer;

       l_fp    tRecvTimestamp;         /* arrival timestamp */
       int     rc ;
       char    *pBuf, sLogText [ MAX_LOGTEXT ] ;
       size_t  iLen, iCopyLen ;
       int     i, j, iReadRawBuf, iBreakPosition ;

       /*
        * Initialize pointers and read the timecode and timestamp
        */
       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       /*
        * Get next input line
        */
       if ( up->linediscipline == LDISC_RAW ) {

               pp->lencode  = refclock_gtraw ( rbufp, pp->a_lastcode, BMAX-1, &tRecvTimestamp ) ;
               /* 3rd argument can be BMAX, but the coverity scan tool claim "Memory - corruptions  (OVERRUN)" */
               /* "a_lastcode" is defined as "char a_lastcode[BMAX]" in the ntp_refclock.h */
               /* To avoid its claim, pass the value BMAX-1. */

               /*
                * Append received characters to temporary buffer
                */
               for ( i = 0 ;
                     i < pp->lencode && up->iRawBufLen < MAX_RAWBUF - 2 ;
                     i ++ , up->iRawBufLen ++ ) {
                       up->sRawBuf[up->iRawBufLen] = pp->a_lastcode[i] ;
               }
               up->sRawBuf[up->iRawBufLen] = 0 ;


       } else {

               pp->lencode  = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;

       }
#ifdef DEBUG
       printf( "\nrefclock_jjy.c : %s : Len=%d  ", sFunctionName, pp->lencode ) ;
       for ( i = 0 ; i < pp->lencode ; i ++ ) {
               if ( iscntrl( (u_char)(pp->a_lastcode[i] & 0x7F) ) ) {
                       printf( "<x%02X>", pp->a_lastcode[i] & 0xFF ) ;
               } else {
                       printf( "%c", pp->a_lastcode[i] ) ;
               }
       }
       printf( "\n" ) ;
#endif

       /*
        * The reply with <CR><LF> gives a blank line
        */

       if ( pp->lencode == 0 ) return ;

       /*
        * Receiving data is not expected
        */

       if ( up->iProcessState == JJY_PROCESS_STATE_IDLE
         || up->iProcessState == JJY_PROCESS_STATE_DONE
         || up->iProcessState == JJY_PROCESS_STATE_ERROR ) {
               /* Discard received data */
               up->iRawBufLen = 0 ;
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : %s : Discard received data\n", sFunctionName ) ;
               }
#endif
               return ;
       }

       /*
        * We get down to business
        */

       pp->lastrec = tRecvTimestamp ;

       up->iLineCount ++ ;

       up->iProcessState = JJY_PROCESS_STATE_RECEIVE ;
       up->bReceiveFlag = TRUE ;

       iReadRawBuf = 0 ;
       iBreakPosition = up->iRawBufLen - 1 ;
       for ( ; up->iProcessState == JJY_PROCESS_STATE_RECEIVE ; ) {

               if ( up->linediscipline == LDISC_RAW ) {

                       if ( up->bWaitBreakString ) {
                               iBreakPosition = getRawDataBreakPosition( up, iReadRawBuf ) ;
                               if ( iBreakPosition == -1 ) {
                                       /* Break string have not come yet */
                                       if ( up->iRawBufLen < MAX_RAWBUF - 2
                                         || iReadRawBuf > 0 ) {
                                               /* Temporary buffer is not full */
                                               break ;
                                       } else {
                                               /* Temporary buffer is full */
                                               iBreakPosition = up->iRawBufLen - 1 ;
                                       }
                               }
                       } else {
                               iBreakPosition = up->iRawBufLen - 1 ;
                       }

                       /* Copy characters from temporary buffer to process buffer */
                       up->iLineBufLen = up->iTextBufLen = 0 ;
                       for ( i = iReadRawBuf ; i <= iBreakPosition ; i ++ ) {

                               /* Copy all characters */
                               up->sLineBuf[up->iLineBufLen] = up->sRawBuf[i] ;
                               up->iLineBufLen ++ ;

                               /* Copy printable characters */
                               if ( ! iscntrl( (u_char)up->sRawBuf[i] ) ) {
                                       up->sTextBuf[up->iTextBufLen] = up->sRawBuf[i] ;
                                       up->iTextBufLen ++ ;
                               }

                       }
                       up->sLineBuf[up->iLineBufLen] = 0 ;
                       up->sTextBuf[up->iTextBufLen] = 0 ;
#ifdef DEBUG
                       printf( "refclock_jjy.c : %s : up->iLineBufLen=%d up->iTextBufLen=%d\n",
                                sFunctionName, up->iLineBufLen, up->iTextBufLen ) ;
#endif

                       if ( up->bSkipCntrlCharOnly && up->iTextBufLen == 0 ) {
#ifdef DEBUG
                               printf( "refclock_jjy.c : %s : Skip cntrl char only : up->iRawBufLen=%d iReadRawBuf=%d iBreakPosition=%d\n",
                                        sFunctionName, up->iRawBufLen, iReadRawBuf, iBreakPosition ) ;
#endif
                               if ( iBreakPosition + 1 < up->iRawBufLen ) {
                                       iReadRawBuf = iBreakPosition + 1 ;
                                       continue ;
                               } else {
                                       break ;
                               }

                       }

               }

               if ( up->linediscipline == LDISC_RAW ) {
                       pBuf = up->sLineBuf ;
                       iLen = up->iLineBufLen ;
               } else {
                       pBuf = pp->a_lastcode ;
                       iLen = pp->lencode ;
               }

               iCopyLen = ( iLen <= sizeof(sLogText)-1 ? iLen : sizeof(sLogText)-1 ) ;
               memcpy( sLogText, pBuf, iCopyLen ) ;
               sLogText[iCopyLen] = '\0' ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_RECEIVE, sLogText ) ;

               switch ( up->unittype ) {

               case UNITTYPE_TRISTATE_JJY01 :
                       rc = jjy_receive_tristate_jjy01  ( rbufp ) ;
                       break ;

               case UNITTYPE_CDEX_JST2000 :
                       rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
                       break ;

               case UNITTYPE_ECHOKEISOKUKI_LT2000 :
                       rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ;
                       break ;

               case UNITTYPE_CITIZENTIC_JJY200 :
                       rc = jjy_receive_citizentic_jjy200 ( rbufp ) ;
                       break ;

               case UNITTYPE_TRISTATE_GPSCLOCK01 :
                       rc = jjy_receive_tristate_gpsclock01 ( rbufp ) ;
                       break ;

               case UNITTYPE_SEIKO_TIMESYS_TDC_300 :
                       rc = jjy_receive_seiko_tsys_tdc_300 ( rbufp ) ;
                       break ;

               case UNITTYPE_TELEPHONE :
                       rc = jjy_receive_telephone ( rbufp ) ;
                       break ;

               default :
                       rc = JJY_RECEIVE_ERROR ;
                       break ;

               }

               switch ( rc ) {
               case JJY_RECEIVE_DONE :
               case JJY_RECEIVE_SKIP :
                       up->iProcessState = JJY_PROCESS_STATE_DONE ;
                       break ;
               case JJY_RECEIVE_ERROR :
                       up->iProcessState = JJY_PROCESS_STATE_ERROR ;
                       break ;
               default :
                       break ;
               }

               if ( up->linediscipline == LDISC_RAW ) {
                       if ( rc == JJY_RECEIVE_UNPROCESS ) {
                               break ;
                       }
                       iReadRawBuf = iBreakPosition + 1 ;
                       if ( iReadRawBuf >= up->iRawBufLen ) {
                               /* Processed all received data */
                               break ;
                       }
               }

               if ( up->linediscipline == LDISC_CLK ) {
                       break ;
               }

       }

       if ( up->linediscipline == LDISC_RAW && iReadRawBuf > 0 ) {
               for ( i = 0, j = iReadRawBuf ; j < up->iRawBufLen ; i ++, j++ ) {
                       up->sRawBuf[i] = up->sRawBuf[j] ;
               }
               up->iRawBufLen -= iReadRawBuf ;
               if ( up->iRawBufLen < 0 ) {
                       up->iRawBufLen = 0 ;
               }
       }

       up->bReceiveFlag = FALSE ;

}

/**************************************************************************************************/

static int
getRawDataBreakPosition ( struct jjyunit *up, int iStart )
{

       int     i, j ;

       if ( iStart >= up->iRawBufLen ) {
#ifdef DEBUG
               printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ;
#endif
               return -1 ;
       }

       for ( i = iStart ; i < up->iRawBufLen ; i ++ ) {

               for ( j = 0 ; up->pRawBreak[j].pString != NULL ; j ++ ) {

                       if ( i + up->pRawBreak[j].iLength <= up->iRawBufLen ) {

                               if ( strncmp( up->sRawBuf + i,
                                       up->pRawBreak[j].pString,
                                       up->pRawBreak[j].iLength ) == 0 ) {

#ifdef DEBUG
                                       printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=%d\n",
                                               iStart, i + up->pRawBreak[j].iLength - 1 ) ;
#endif
                                       return i + up->pRawBreak[j].iLength - 1 ;

                               }
                       }
               }
       }

#ifdef DEBUG
       printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ;
#endif
       return -1 ;

}

/**************************************************************************************************/
/*  jjy_poll - called by the transmit procedure                                                   */
/**************************************************************************************************/
static void
jjy_poll ( int unit, struct peer *peer )
{

       char    sLog [ 40 ], sReach [ 9 ] ;

       struct jjyunit      *up;
       struct refclockproc *pp;

       pp = peer->procptr;
       up = pp->unitptr ;

       if ( up->bInitError ) {
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, "Ignore polling because of error during initializing" ) ;
               return ;
       }

       if ( pp->polls > 0  &&  up->iLineCount == 0 ) {
               /*
                * No reply for last command
                */
               refclock_report ( peer, CEVNT_TIMEOUT ) ;
       }

       pp->polls ++ ;

       sReach[0] = peer->reach & 0x80 ? '1' : '0' ;
       sReach[1] = peer->reach & 0x40 ? '1' : '0' ;
       sReach[2] = peer->reach & 0x20 ? '1' : '0' ;
       sReach[3] = peer->reach & 0x10 ? '1' : '0' ;
       sReach[4] = peer->reach & 0x08 ? '1' : '0' ;
       sReach[5] = peer->reach & 0x04 ? '1' : '0' ;
       sReach[6] = peer->reach & 0x02 ? '1' : '0' ;
       sReach[7] = 0 ; /* This poll */
       sReach[8] = 0 ;

       snprintf( sLog, sizeof(sLog), "polls=%ld reach=%s", pp->polls, sReach ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ;

       up->iProcessState = JJY_PROCESS_STATE_POLL ;
       up->iCommandSeq = 0 ;
       up->iReceiveSeq = 0 ;
       up->iLineCount = 0 ;
       up->bLineError = FALSE ;
       up->iRawBufLen = 0 ;

       switch ( up->unittype ) {

       case UNITTYPE_TRISTATE_JJY01 :
               jjy_poll_tristate_jjy01  ( unit, peer ) ;
               break ;

       case UNITTYPE_CDEX_JST2000 :
               jjy_poll_cdex_jst2000 ( unit, peer ) ;
               break ;

       case UNITTYPE_ECHOKEISOKUKI_LT2000 :
               jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ;
               break ;

       case UNITTYPE_CITIZENTIC_JJY200 :
               jjy_poll_citizentic_jjy200 ( unit, peer ) ;
               break ;

       case UNITTYPE_TRISTATE_GPSCLOCK01 :
               jjy_poll_tristate_gpsclock01 ( unit, peer ) ;
               break ;

       case UNITTYPE_SEIKO_TIMESYS_TDC_300 :
               jjy_poll_seiko_tsys_tdc_300 ( unit, peer ) ;
               break ;

       case UNITTYPE_TELEPHONE :
               jjy_poll_telephone ( unit, peer ) ;
               break ;

       default :
               break ;

       }

}

/**************************************************************************************************/
/*  jjy_timer - called at one-second intervals                                                    */
/**************************************************************************************************/
static void
jjy_timer ( int unit, struct peer *peer )
{

       struct  refclockproc *pp ;
       struct  jjyunit      *up ;

#ifdef DEBUG
       if ( debug ) {
               printf ( "refclock_jjy.c : jjy_timer\n" ) ;
       }
#endif

       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->bReceiveFlag ) {
#ifdef DEBUG
               if ( debug ) {
                       printf ( "refclock_jjy.c : jjy_timer : up->bReceiveFlag= TRUE : Timer skipped.\n" ) ;
               }
#endif
               return ;
       }

       switch ( up->unittype ) {

       case UNITTYPE_TELEPHONE :
               jjy_timer_telephone ( unit, peer ) ;
               break ;

       default :
               break ;

       }

}

/**************************************************************************************************/
/*  jjy_synctime                                                                                  */
/**************************************************************************************************/
static void
jjy_synctime ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       char    sLog [ 192 ], cStatus ;
       const char      *pStatus ;

       pp->year   = up->year ;
       pp->day    = ymd2yd( up->year, up->month, up->day ) ;
       pp->hour   = up->hour ;
       pp->minute = up->minute ;
       pp->second = up->second ;
       pp->nsec   = up->msecond * 1000000 ;

       /*
        * JST to UTC
        */
       pp->hour -= 9 ;
       if ( pp->hour < 0 ) {
               pp->hour += 24 ;
               pp->day -- ;
               if ( pp->day < 1 ) {
                       pp->year -- ;
                       pp->day  = ymd2yd( pp->year, 12, 31 ) ;
               }
       }

       /*
        * Process the new sample in the median filter and determine the
        * timecode timestamp.
        */

       if ( ! refclock_process( pp ) ) {
               refclock_report( peer, CEVNT_BADTIME ) ;
               return ;
       }

       pp->lastref = pp->lastrec ;

       refclock_receive( peer ) ;

       /*
        * Write into the clockstats file
        */
       snprintf ( sLog, sizeof(sLog),
                  "%04d/%02d/%02d %02d:%02d:%02d.%03d JST   ( %04d/%03d %02d:%02d:%02d.%03d UTC )",
                  up->year, up->month, up->day,
                  up->hour, up->minute, up->second, up->msecond,
                  pp->year, pp->day, pp->hour, pp->minute, pp->second,
                  (int)(pp->nsec/1000000) ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ;

       cStatus = ' ' ;
       pStatus = "" ;

       switch ( peer->status ) {
       case 0 : cStatus = ' ' ; pStatus = "Reject"    ; break ;
       case 1 : cStatus = 'x' ; pStatus = "FalseTick" ; break ;
       case 2 : cStatus = '.' ; pStatus = "Excess"    ; break ;
       case 3 : cStatus = '-' ; pStatus = "Outlier"   ; break ;
       case 4 : cStatus = '+' ; pStatus = "Candidate" ; break ;
       case 5 : cStatus = '#' ; pStatus = "Selected"  ; break ;
       case 6 : cStatus = '*' ; pStatus = "Sys.Peer"  ; break ;
       case 7 : cStatus = 'o' ; pStatus = "PPS.Peer"  ; break ;
       default : break ;
       }

       snprintf ( sLog, sizeof(sLog),
                  "status %d [%c] %s : offset %3.3f mSec. : jitter %3.3f mSec.",
                   peer->status, cStatus, pStatus, peer->offset * 1000, peer->jitter * 1000 ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The Tristate Ltd. JJY receiver TS-JJY01, TS-JJY02                                       ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 1                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  dcst<CR><LF>          VALID<CR><LF> or INVALID<CR><LF>                                        */
/*  stus<CR><LF>          ADJUSTED<CR><LF> or UNADJUSTED<CR><LF>                                  */
/*  date<CR><LF>          YYYY/MM/DD XXX<CR><LF>                    XXX is the day of the week    */
/*  time<CR><LF>          HH:MM:SS<CR><LF>                          Not used by this driver       */
/*  stim<CR><LF>          HH:MM:SS<CR><LF>                          Reply at just second          */
/*                                                                                                */
/*################################################################################################*/

#define TS_JJY01_COMMAND_NUMBER_DATE    1
#define TS_JJY01_COMMAND_NUMBER_TIME    2
#define TS_JJY01_COMMAND_NUMBER_STIM    3
#define TS_JJY01_COMMAND_NUMBER_STUS    4
#define TS_JJY01_COMMAND_NUMBER_DCST    5

#define TS_JJY01_REPLY_DATE             "yyyy/mm/dd www"
#define TS_JJY01_REPLY_STIM             "hh:mm:ss"
#define TS_JJY01_REPLY_STUS_ADJUSTED    "adjusted"
#define TS_JJY01_REPLY_STUS_UNADJUSTED  "unadjusted"
#define TS_JJY01_REPLY_DCST_VALID       "valid"
#define TS_JJY01_REPLY_DCST_INVALID     "invalid"

#define TS_JJY01_REPLY_LENGTH_DATE              14      /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_TIME              8       /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_STIM              8       /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED     8       /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED   10      /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_DCST_VALID        5       /* Length without <CR><LF> */
#define TS_JJY01_REPLY_LENGTH_DCST_INVALID      7       /* Length without <CR><LF> */

static  struct
{
       const char      commandNumber ;
       const char      *command ;
       int     commandLength ;
       int     iExpectedReplyLength [ 2 ] ;
} tristate_jjy01_command_sequence[] =
{
       { 0, NULL, 0, { 0, 0 } }, /* Idle */
       { TS_JJY01_COMMAND_NUMBER_DCST, "dcst\r\n", 6, { TS_JJY01_REPLY_LENGTH_DCST_VALID   , TS_JJY01_REPLY_LENGTH_DCST_INVALID } },
       { TS_JJY01_COMMAND_NUMBER_STUS, "stus\r\n", 6, { TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED, TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED } },
       { TS_JJY01_COMMAND_NUMBER_TIME, "time\r\n", 6, { TS_JJY01_REPLY_LENGTH_TIME         , TS_JJY01_REPLY_LENGTH_TIME } },
       { TS_JJY01_COMMAND_NUMBER_DATE, "date\r\n", 6, { TS_JJY01_REPLY_LENGTH_DATE         , TS_JJY01_REPLY_LENGTH_DATE } },
       { TS_JJY01_COMMAND_NUMBER_STIM, "stim\r\n", 6, { TS_JJY01_REPLY_LENGTH_STIM         , TS_JJY01_REPLY_LENGTH_STIM } },
       /* End of command */
       { 0, NULL, 0, { 0, 0 } }
} ;

/**************************************************************************************************/

static int
jjy_start_tristate_jjy01 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-JJY01, TS-JJY02" ) ;

       up->unittype  = UNITTYPE_TRISTATE_JJY01 ;
       up->linespeed = SPEED232_TRISTATE_JJY01 ;
       up->linediscipline = LDISC_CLK ;

       time( &(up->tLastAdjustedTimestamp) ) ;
       up->bStusReplyAdjustedAtLeastOnce = FALSE ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
{
       struct jjyunit      *up ;
       struct refclockproc *pp ;
       struct peer         *peer;

       char *  pBuf ;
       char    sLog [ MAX_LOGTEXT ] ;
       int     iLen ;
       int     rc ;
       time_t  now ;
       double  fSeconds ;

       const char *    pCmd ;
       int             iCmdLen ;

       /* Initialize pointers  */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_jjy01", iLen ) ;

       /* Check expected reply */

       if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) {
               /* Command sequence has not been started, or has been completed */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* Check reply length */

       if ( iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[0]
         && iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[1] ) {
               /* Unexpected reply length */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* Parse reply */

       switch ( tristate_jjy01_command_sequence[up->iCommandSeq].commandNumber ) {

       case TS_JJY01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD WWW */

               rc = sscanf ( pBuf, "%4d/%2d/%2d",
                             &up->year, &up->month, &up->day ) ;

               if ( rc != 3 || up->year < 2000 || 2099 <= up->year
                 || up->month < 1 || 12 < up->month
                 || up->day < 1 || 31 < up->day ) {
                       /* Invalid date */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
                                 rc, up->year, up->month, up->day ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       case TS_JJY01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
       case TS_JJY01_COMMAND_NUMBER_STIM : /* HH:MM:SS */

               if ( up->iTimestampCount >= 2 ) {
                       /* Too many time reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY,
                                 up->iTimestampCount ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               rc = sscanf ( pBuf, "%2d:%2d:%2d",
                             &up->hour, &up->minute, &up->second ) ;

               if ( rc != 3 || up->hour > 23 || up->minute > 59 ||
                    up->second > 60 ) {
                       /* Invalid time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
                                 rc, up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

               up->iTimestampCount++ ;

               up->msecond = 0 ;

               break ;

       case TS_JJY01_COMMAND_NUMBER_STUS :

               if ( strncmp( pBuf, TS_JJY01_REPLY_STUS_ADJUSTED,
                            TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED ) == 0 ) {
                       /* STUS reply : adjusted */
                       up->bStusReplyAdjusted = TRUE ;
                       up->bStusReplyAdjustedAtLeastOnce = TRUE ;
                       time( &(up->tLastAdjustedTimestamp) ) ;
               } else if ( strncmp( pBuf, TS_JJY01_REPLY_STUS_UNADJUSTED,
                                    TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED ) == 0 ) {
                       /* STUS reply : unadjusted */
                       up->bStusReplyAdjusted = FALSE ;
               } else {
                       /* Bad reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                                 pBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       case TS_JJY01_COMMAND_NUMBER_DCST :

               if ( strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID,
                             TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0
                 || strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID,
                             TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) {
                       /* Valid reply */
               } else {
                       /* Bad reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                                 pBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       default : /* Unexpected reply */

               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;

       }

       if ( up->iTimestampCount == 2 ) {
               /* Process date and time */

               time( &now ) ;
               fSeconds = difftime( now, up->tLastAdjustedTimestamp ) ;

               if ( ( pp->sloppyclockflag & CLK_FLAG2 ) != 0
                 && ( ! up->bStusReplyAdjusted )
                 && ( fSeconds >= ( pp->fudgetime2 * 3600 ) || ( ! up->bStusReplyAdjustedAtLeastOnce ) ) ) {
                       /* STUS is not ADJUSTED */
                       if ( fSeconds < 60 ) {
                               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_STUS_UNADJUSTED, fSeconds, "seconds" ) ;
                       } else if ( fSeconds < 3600 ) {
                               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_STUS_UNADJUSTED, fSeconds / 60, "minutes" ) ;
                       } else if ( fSeconds < 86400 ) {
                               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_STUS_UNADJUSTED, fSeconds / 3600, "hours" ) ;
                       } else {
                               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_STUS_UNADJUSTED, fSeconds / 86400, "days" ) ;
                       }
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
                       return JJY_RECEIVE_SKIP ;
               } else if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0]
                        && up->iTimestamp[0]     <= up->iTimestamp[1] ) {
                       /* 3 commands (time,date,stim) was executed in two seconds */
                       jjy_synctime( peer, pp, up ) ;
                       return JJY_RECEIVE_DONE ;
               } else if ( up->iTimestamp[0] > up->iTimestamp[1] ) {
                       /* Over midnight, and date is unsure */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2,
                                 up->iTimestamp[0], up->iTimestamp[1] ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
                       return JJY_RECEIVE_SKIP ;
               } else {
                       /* Slow reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2,
                                 up->iTimestamp[0], up->iTimestamp[1] ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

       }

       /* Issue next command */

       if ( tristate_jjy01_command_sequence[up->iCommandSeq].command != NULL ) {
               up->iCommandSeq ++ ;
       }

       if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) {
               /* Command sequence completed */
               return JJY_RECEIVE_DONE ;
       }

       pCmd =  tristate_jjy01_command_sequence[up->iCommandSeq].command ;
       iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ;
       if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_tristate_jjy01  ( int unit, struct peer *peer )
{
#ifdef DEBUG
       static const char *sFunctionName = "jjy_poll_tristate_jjy01" ;
#endif

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       const char *    pCmd ;
       int             iCmdLen ;

       pp = peer->procptr;
       up = pp->unitptr ;

       up->bLineError = FALSE ;
       up->iTimestampCount = 0 ;

       if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
               /* Skip "dcst" and "stus" commands */
               up->iCommandSeq = 2 ;
               up->iLineCount = 2 ;
       }

       up->bStusReplyAdjusted = FALSE ;

#ifdef DEBUG
       if ( debug ) {
               printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->iLineCount=%d\n",
                       sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
                       up->iLineCount ) ;
       }
#endif

       /*
        * Send a first command
        */

       up->iCommandSeq ++ ;

       pCmd =  tristate_jjy01_command_sequence[up->iCommandSeq].command ;
       iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ;
       if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The C-DEX Co. Ltd. JJY receiver JST2000                                                 ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 2                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  <ENQ>1J<ETX>          <STX>JYYMMDDWHHMMSSS<ETX>                 J is a fixed character        */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak cdex_jst2000_raw_break [ ] =
{
       { "\x03", 1 }, { NULL, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_cdex_jst2000 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: C-DEX Co. Ltd. JST2000" ) ;

       up->unittype  = UNITTYPE_CDEX_JST2000 ;
       up->linespeed = SPEED232_CDEX_JST2000 ;
       up->linediscipline = LDISC_RAW ;

       up->pRawBreak = cdex_jst2000_raw_break ;
       up->bWaitBreakString = TRUE ;

       up->bSkipCntrlCharOnly = FALSE ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
{

       struct jjyunit      *up ;
       struct refclockproc *pp ;
       struct peer         *peer ;

       char    *pBuf, sLog [ MAX_LOGTEXT ] ;
       int     iLen ;
       int     rc ;

       /* Initialize pointers */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_cdex_jst2000", iLen ) ;

       /* Check expected reply */

       if ( up->iCommandSeq != 1 ) {
               /* Command sequence has not been started, or has been completed */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* Wait until ETX comes */

       if ( up->iLineBufLen < 17 || up->sLineBuf[up->iLineBufLen-1] != 0x03 ) {
               return JJY_RECEIVE_UNPROCESS ;
       }

       /* Check reply length */

       if ( iLen != 15 ) {
               /* Unexpected reply length */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* JYYMMDDWHHMMSSS */

       rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
                     &up->year, &up->month, &up->day,
                     &up->hour, &up->minute, &up->second,
                     &up->msecond ) ;

       if ( rc != 7 || up->month < 1 || up->month > 12 ||
            up->day < 1 || up->day > 31 || up->hour > 23 ||
            up->minute > 59 || up->second > 60 ) {
               /* Invalid date and time */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
                         rc, up->year, up->month, up->day,
                         up->hour, up->minute, up->second ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       up->year    += 2000 ;
       up->msecond *= 100 ;

       jjy_synctime( peer, pp, up ) ;

       return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
{

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       up->bLineError = FALSE ;
       up->iRawBufLen = 0 ;
       up->iLineBufLen = 0 ;
       up->iTextBufLen = 0 ;

       /*
        * Send "<ENQ>1J<ETX>" command
        */

       up->iCommandSeq ++ ;

       if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4  ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\0051J\003" ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The Echo Keisokuki Co. Ltd. JJY receiver LT2000                                         ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 3                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  #                                                               Mode 1 ( Request & Send )     */
/*  T                     YYMMDDWHHMMSS<BCC1><BCC2><CR>                                           */
/*  C                                                               Mode 2 ( Continuous )         */
/*                        YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR>     0.5 sec before time stamp     */
/*                        <SUB>                                     Second signal                 */
/*                                                                                                */
/*################################################################################################*/

#define ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND          1
#define ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS            2
#define ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS  3

#define ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND       "#"
#define ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_TIME       "T"
#define ECHOKEISOKUKI_LT2000_COMMAND_CONTINUOUS         "C"

/**************************************************************************************************/

static int
jjy_start_echokeisokuki_lt2000 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Echo Keisokuki Co. Ltd. LT2000" ) ;

       up->unittype  = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
       up->linespeed = SPEED232_ECHOKEISOKUKI_LT2000 ;
       up->linediscipline = LDISC_CLK ;

       up->operationmode = ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
{

       struct jjyunit      *up ;
       struct refclockproc *pp ;
       struct peer         *peer;

       char    *pBuf, sLog [ 100 ], sErr [ 60 ] ;
       int     iLen ;
       int     rc ;
       int     i, ibcc, ibcc1, ibcc2 ;

       /* Initialize pointers */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_echokeisokuki_lt2000", iLen ) ;

       /* Check reply length */

       if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND
              && iLen != 15 )
         || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
              && iLen != 17 )
         || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS
              && iLen != 17 ) ) {
               /* Unexpected reply length */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen == 15 ) {
               /* YYMMDDWHHMMSS<BCC1><BCC2> */

               for ( i = ibcc = 0 ; i < 13 ; i ++ ) {
                       ibcc ^= pBuf[i] ;
               }

               ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ;
               ibcc2 = 0x30 | ( ( ibcc      ) & 0xF ) ;
               if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) {
                       snprintf( sErr, sizeof(sErr)-1, " BCC error : Recv=%02X,%02X / Calc=%02X,%02X ",
                                 pBuf[13] & 0xFF, pBuf[14] & 0xFF,
                                 ibcc1, ibcc2 ) ;
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                                 sErr ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

       }

       if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND
              && iLen == 15 )
         || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
              && iLen == 17 )
         || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS
              && iLen == 17 ) ) {
               /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */

               rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d",
                             &up->year, &up->month, &up->day,
                             &up->hour, &up->minute, &up->second ) ;

               if ( rc != 6 || up->month < 1 || up->month > 12
                 || up->day < 1 || up->day > 31
                 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid date and time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
                                 rc, up->year, up->month, up->day,
                                 up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               up->year += 2000 ;

               if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
                 || up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) {
                       /* A time stamp comes on every 0.5 second in the mode 2 of the LT-2000. */

                       up->msecond = 500 ;
                       up->second -- ;
                       if ( up->second < 0 ) {
                               up->second = 59 ;
                               up->minute -- ;
                               if ( up->minute < 0 ) {
                                       up->minute = 59 ;
                                       up->hour -- ;
                                       if ( up->hour < 0 ) {
                                               up->hour = 23 ;
                                               up->day -- ;
                                               if ( up->day < 1 ) {
                                                       up->month -- ;
                                                       if ( up->month < 1 ) {
                                                               up->month = 12 ;
                                                               up->year -- ;
                                                       }
                                               }
                                       }
                               }
                       }

               }

               jjy_synctime( peer, pp, up ) ;


       }

       if (up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) {
               /* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */

               iLen = strlen( ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ;
               if ( write ( pp->io.fd, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND, iLen ) != iLen  ) {
                       refclock_report ( peer, CEVNT_FAULT ) ;
               }

               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ;

       }

       return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer )
{

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       char    sCmd[2] ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       up->bLineError = FALSE ;

       /*
        * Send "T" or "C" command
        */

       switch ( up->operationmode ) {
       case ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND :
               sCmd[0] = 'T' ;
               break ;
       case ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS :
       case ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS :
               sCmd[0] = 'C' ;
               break ;
       }
       sCmd[1] = 0 ;

       if ( write ( pp->io.fd, sCmd, 1 ) != 1  ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The CITIZEN T.I.C CO., LTD. JJY receiver JJY200                                         ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 4                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*                        'XX YY/MM/DD W HH:MM:SS<CR>               XX:OK|NG|ER  W:0(Mon)-6(Sun)  */
/*                                                                                                */
/*################################################################################################*/

static int
jjy_start_citizentic_jjy200 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: CITIZEN T.I.C CO. LTD. JJY200" ) ;

       up->unittype  = UNITTYPE_CITIZENTIC_JJY200 ;
       up->linespeed = SPEED232_CITIZENTIC_JJY200 ;
       up->linediscipline = LDISC_CLK ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp )
{

       struct jjyunit          *up ;
       struct refclockproc     *pp ;
       struct peer             *peer;

       char    *pBuf, sLog [ 100 ], sMsg [ 16 ] ;
       int     iLen ;
       int     rc ;
       char    cApostrophe, sStatus[3] ;
       int     iWeekday ;

       /* Initialize pointers */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_citizentic_jjy200", iLen ) ;

       /*
        * JJY-200 sends a timestamp every second.
        * So, a timestamp is ignored unless it is right after polled.
        */

       if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) {
               return JJY_RECEIVE_SKIP ;
       }

       /* Check reply length */

       if ( iLen != 23 ) {
               /* Unexpected reply length */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* 'XX YY/MM/DD W HH:MM:SS<CR> */

       rc = sscanf ( pBuf, "%c%2s %2d/%2d/%2d %1d %2d:%2d:%2d",
                     &cApostrophe, sStatus,
                     &up->year, &up->month, &up->day, &iWeekday,
                     &up->hour, &up->minute, &up->second ) ;
       sStatus[2] = 0 ;

       if ( rc != 9 || cApostrophe != '\''
         || ( strcmp( sStatus, "OK" ) != 0
           && strcmp( sStatus, "NG" ) != 0
           && strcmp( sStatus, "ER" ) != 0 )
         || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
         || iWeekday > 6
         || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
               /* Invalid date and time */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
                         rc, up->year, up->month, up->day,
                         up->hour, up->minute, up->second ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       } else if ( strcmp( sStatus, "NG" ) == 0
                || strcmp( sStatus, "ER" ) == 0 ) {
               /* Timestamp is unsure */
               snprintf( sMsg, sizeof(sMsg)-1, "status=%s", sStatus ) ;
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE,
                         sMsg ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;
               return JJY_RECEIVE_SKIP ;
       }

       up->year += 2000 ;
       up->msecond = 0 ;

       jjy_synctime( peer, pp, up ) ;

       return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_citizentic_jjy200 ( int unit, struct peer *peer )
{

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       up->bLineError = FALSE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The Tristate Ltd. GPS clock TS-GPS01                                                    ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 5                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  This clock has NMEA mode and command/response mode.                                           */
/*  When this jjy driver are used, set to command/response mode of this clock                     */
/*  by the onboard switch SW4, and make sure the LED-Y is tured on.                               */
/*  Other than this JJY driver, the refclock driver type 20, generic NMEA driver,                 */
/*  works with the NMEA mode of this clock.                                                       */
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  stus<CR><LF>          *R|*G|*U|+U<CR><LF>                                                     */
/*  date<CR><LF>          YY/MM/DD<CR><LF>                                                        */
/*  time<CR><LF>          HH:MM:SS<CR><LF>                                                        */
/*                                                                                                */
/*################################################################################################*/

#define TS_GPS01_COMMAND_NUMBER_DATE    1
#define TS_GPS01_COMMAND_NUMBER_TIME    2
#define TS_GPS01_COMMAND_NUMBER_STUS    4

#define TS_GPS01_REPLY_DATE             "yyyy/mm/dd"
#define TS_GPS01_REPLY_TIME             "hh:mm:ss"
#define TS_GPS01_REPLY_STUS_RTC         "*R"
#define TS_GPS01_REPLY_STUS_GPS         "*G"
#define TS_GPS01_REPLY_STUS_UTC         "*U"
#define TS_GPS01_REPLY_STUS_PPS         "+U"

#define TS_GPS01_REPLY_LENGTH_DATE          10  /* Length without <CR><LF> */
#define TS_GPS01_REPLY_LENGTH_TIME          8   /* Length without <CR><LF> */
#define TS_GPS01_REPLY_LENGTH_STUS          2   /* Length without <CR><LF> */

static  struct
{
       char    commandNumber ;
       const char      *command ;
       int     commandLength ;
       int     iExpectedReplyLength ;
} tristate_gps01_command_sequence[] =
{
       { 0, NULL, 0, 0 }, /* Idle */
       { TS_GPS01_COMMAND_NUMBER_STUS, "stus\r\n", 6, TS_GPS01_REPLY_LENGTH_STUS },
       { TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME },
       { TS_GPS01_COMMAND_NUMBER_DATE, "date\r\n", 6, TS_GPS01_REPLY_LENGTH_DATE },
       { TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME },
       /* End of command */
       { 0, NULL, 0, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_tristate_gpsclock01 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-GPS01" ) ;

       up->unittype  = UNITTYPE_TRISTATE_GPSCLOCK01 ;
       up->linespeed = SPEED232_TRISTATE_GPSCLOCK01 ;
       up->linediscipline = LDISC_CLK ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_tristate_gpsclock01 ( struct recvbuf *rbufp )
{
#ifdef DEBUG
       static  const char      *sFunctionName = "jjy_receive_tristate_gpsclock01" ;
#endif

       struct jjyunit      *up ;
       struct refclockproc *pp ;
       struct peer         *peer;

       char *          pBuf ;
       char            sLog [ MAX_LOGTEXT ] ;
       int             iLen ;
       int             rc ;

       const char *    pCmd ;
       int             iCmdLen ;

       /* Initialize pointers */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_gpsclock01", iLen ) ;

       /* Ignore NMEA data stream */

       if ( iLen > 5
         && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
#ifdef DEBUG
               if ( debug ) {
                       printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
                               sFunctionName, pBuf ) ;
               }
#endif
               return JJY_RECEIVE_WAIT ;
       }

       /*
        * Skip command prompt '$Cmd>' from the TS-GPSclock-01
        */
       if ( iLen == 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
               return JJY_RECEIVE_WAIT ;
       } else if ( iLen > 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
               pBuf += 5 ;
               iLen -= 5 ;
       }

       /*
        * Ignore NMEA data stream after command prompt
        */
       if ( iLen > 5
         && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
#ifdef DEBUG
               if ( debug ) {
                       printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
                               sFunctionName, pBuf ) ;
               }
#endif
               return JJY_RECEIVE_WAIT ;
       }

       /* Check expected reply */

       if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
               /* Command sequence has not been started, or has been completed */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* Check reply length */

       if ( iLen != tristate_gps01_command_sequence[up->iCommandSeq].iExpectedReplyLength ) {
               /* Unexpected reply length */
               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;
       }

       /* Parse reply */

       switch ( tristate_gps01_command_sequence[up->iCommandSeq].commandNumber ) {

       case TS_GPS01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD */

               rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;

               if ( rc != 3 || up->year < 2000 || 2099 <= up->year
                 || up->month < 1 || 12 < up->month
                 || up->day < 1 || 31 < up->day ) {
                       /* Invalid date */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
                                 rc, up->year, up->month, up->day ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       case TS_GPS01_COMMAND_NUMBER_TIME : /* HH:MM:SS */

               if ( up->iTimestampCount >= 2 ) {
                       /* Too many time reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY,
                                 up->iTimestampCount ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               rc = sscanf ( pBuf, "%2d:%2d:%2d",
                             &up->hour, &up->minute, &up->second ) ;

               if ( rc != 3
                 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
                                 rc, up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

               up->iTimestampCount++ ;

               up->msecond = 0 ;

               break ;

       case TS_GPS01_COMMAND_NUMBER_STUS :

               if ( strncmp( pBuf, TS_GPS01_REPLY_STUS_RTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0
                 || strncmp( pBuf, TS_GPS01_REPLY_STUS_GPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0
                 || strncmp( pBuf, TS_GPS01_REPLY_STUS_UTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0
                 || strncmp( pBuf, TS_GPS01_REPLY_STUS_PPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0 ) {
                       /* Good */
               } else {
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                                 pBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       default : /*  Unexpected reply */

               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;

       }

       if ( up->iTimestampCount == 2 ) {
               /* Process date and time */

               if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0]
                 && up->iTimestamp[0]     <= up->iTimestamp[1] ) {
                       /* 3 commands (time,date,stim) was executed in two seconds */
                       jjy_synctime( peer, pp, up ) ;
                       return JJY_RECEIVE_DONE ;
               } else if ( up->iTimestamp[0] > up->iTimestamp[1] ) {
                       /* Over midnight, and date is unsure */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2,
                                 up->iTimestamp[0], up->iTimestamp[1] ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
                       return JJY_RECEIVE_SKIP ;
               } else {
                       /* Slow reply */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2,
                                 up->iTimestamp[0], up->iTimestamp[1] ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

       }

       if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
               /* Command sequence completed */
               jjy_synctime( peer, pp, up ) ;
               return JJY_RECEIVE_DONE ;
       }

       /* Issue next command */

       if ( tristate_gps01_command_sequence[up->iCommandSeq].command != NULL ) {
               up->iCommandSeq ++ ;
       }

       if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
               /* Command sequence completed */
               up->iProcessState = JJY_PROCESS_STATE_DONE ;
               return JJY_RECEIVE_DONE ;
       }

       pCmd =  tristate_gps01_command_sequence[up->iCommandSeq].command ;
       iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ;
       if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_tristate_gpsclock01 ( int unit, struct peer *peer )
{
#ifdef DEBUG
       static const char *sFunctionName = "jjy_poll_tristate_gpsclock01" ;
#endif

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       const char *    pCmd ;
       int             iCmdLen ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       up->iTimestampCount = 0 ;

       if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
               /* Skip "stus" command */
               up->iCommandSeq = 1 ;
               up->iLineCount = 1 ;
       }

#ifdef DEBUG
       if ( debug ) {
               printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->iLineCount=%d\n",
                       sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
                       up->iLineCount ) ;
       }
#endif

       /*
        * Send a first command
        */

       up->iCommandSeq ++ ;

       pCmd =  tristate_gps01_command_sequence[up->iCommandSeq].command ;
       iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ;
       if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report ( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    The SEIKO TIME SYSTEMS TDC-300                                                          ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 6                                                            ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Type                  Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  Type 1                <STX>HH:MM:SS<ETX>                                                      */
/*  Type 2                <STX>YYMMDDHHMMSSWLSCU<ETX>               W:0(Sun)-6(Sat)               */
/*  Type 3                <STX>YYMMDDWHHMMSS<ETX>                   W:0(Sun)-6(Sat)               */
/*                        <STX><xE5><ETX>                           5 to 10 mSec. before second   */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak seiko_tsys_tdc_300_raw_break [ ] =
{
       { "\x03", 1 }, { NULL, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_seiko_tsys_tdc_300 ( int unit, struct peer *peer, struct jjyunit *up )
{

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: SEIKO TIME SYSTEMS TDC-300" ) ;

       up->unittype  = UNITTYPE_SEIKO_TIMESYS_TDC_300 ;
       up->linespeed = SPEED232_SEIKO_TIMESYS_TDC_300 ;
       up->linediscipline = LDISC_RAW ;

       up->pRawBreak = seiko_tsys_tdc_300_raw_break ;
       up->bWaitBreakString = TRUE ;

       up->bSkipCntrlCharOnly = FALSE ;

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_seiko_tsys_tdc_300 ( struct recvbuf *rbufp )
{

       struct peer             *peer;
       struct refclockproc     *pp ;
       struct jjyunit          *up ;

       char    *pBuf, sLog [ MAX_LOGTEXT ] ;
       int     iLen, i ;
       int     rc, iWeekday ;
       time_t  now ;
       struct  tm      *pTime ;

       /* Initialize pointers */

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_seiko_tsys_tdc_300", iLen ) ;

       /*
        * TDC-300 sends a timestamp every second.
        * So, a timestamp is ignored unless it is right after polled.
        */

       if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) {
               return JJY_RECEIVE_SKIP ;
       }

       /* Process timestamp */

       up->iReceiveSeq ++ ;

       switch ( iLen ) {

       case 8 : /* Type 1 : <STX>HH:MM:SS<ETX> */

               for ( i = 0 ; i < iLen ; i ++ ) {
                       pBuf[i] &= 0x7F ;
               }

               rc = sscanf ( pBuf+1, "%2d:%2d:%2d",
                     &up->hour, &up->minute, &up->second ) ;

               if ( rc != 3
                 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
                                 rc, up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               } else if ( up->hour == 23 && up->minute == 59 && up->second >= 55 ) {
                       /* Uncertainty date guard */
                       return JJY_RECEIVE_WAIT ;
               }

               time( &now ) ;
               pTime = localtime( &now ) ;
               up->year  = pTime->tm_year ;
               up->month = pTime->tm_mon + 1 ;
               up->day   = pTime->tm_mday ;

               break ;

       case 17 : /* Type 2 : <STX>YYMMDDHHMMSSWLSCU<ETX> */

               for ( i = 0 ; i < iLen ; i ++ ) {
                       pBuf[i] &= 0x7F ;
               }

               rc = sscanf ( pBuf+1, "%2d%2d%2d%2d%2d%2d%1d",
                     &up->year, &up->month, &up->day,
                     &up->hour, &up->minute, &up->second, &iWeekday ) ;

               if ( rc != 7
                 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
                 || iWeekday > 6
                 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid date and time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
                                 rc, up->year, up->month, up->day,
                                 up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       case 13 : /* Type 3 : <STX>YYMMDDWHHMMSS<ETX> */

               rc = sscanf ( pBuf, "%2d%2d%2d%1d%2d%2d%2d",
                     &up->year, &up->month, &up->day, &iWeekday,
                     &up->hour, &up->minute, &up->second ) ;

               if ( rc != 7
                 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
                 || iWeekday > 6
                 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid date and time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
                                 rc, up->year, up->month, up->day,
                                 up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               return JJY_RECEIVE_WAIT ;

       case 1 : /* Type 3 : <STX><xE5><ETX> */

               if ( ( *pBuf & 0xFF ) != 0xE5 ) {
                       /* Invalid second signal */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
                                 up->sLineBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               } else if ( up->iReceiveSeq == 1 ) {
                       /* Wait for next timestamp */
                       up->iReceiveSeq -- ;
                       return JJY_RECEIVE_WAIT ;
               } else if ( up->iReceiveSeq >= 3 ) {
                       /* Unexpected second signal */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
                                 up->sLineBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
                       return JJY_RECEIVE_ERROR ;
               }

               break ;

       default : /* Unexpected reply length */

               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
                         iLen ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
               up->bLineError = TRUE ;
               return JJY_RECEIVE_ERROR ;

       }

       up->year += 2000 ;
       up->msecond = 0 ;

       jjy_synctime( peer, pp, up ) ;

       return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_seiko_tsys_tdc_300 ( int unit, struct peer *peer )
{

       struct refclockproc *pp ;
       struct jjyunit      *up ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       up->bLineError = FALSE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    Telephone JJY                                                                           ##*/
/*##                                                                                            ##*/
/*##    server  127.127.40.X  mode 100 to 180                                                   ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Prompt                Command               Response              Remarks                     */
/*  --------------------  --------------------  --------------------  --------------------------  */
/*  Name<SP>?<SP>         TJJY<CR>              Welcome messages      TJJY is a guest user ID     */
/*  >                     4DATE<CR>             YYYYMMDD<CR>                                      */
/*  >                     LEAPSEC<CR>           XX<CR>                One of <SP>0, +1, -1        */
/*  >                     TIME<CR>              HHMMSS<CR>            3 times on second           */
/*  >                     BYE<CR>               Sayounara messages                                */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak teljjy_raw_break [ ] =
{
       { "\r\n", 2 },
       { "\r"  , 1 },
       { "\n"  , 1 },
       { "Name ? ", 7 },
       { ">"   , 1 },
       { "+++" , 3 },
       { NULL  , 0 }
} ;

#define TELJJY_STATE_IDLE       0
#define TELJJY_STATE_DAILOUT    1
#define TELJJY_STATE_LOGIN      2
#define TELJJY_STATE_CONNECT    3
#define TELJJY_STATE_BYE        4

#define TELJJY_EVENT_NULL       0
#define TELJJY_EVENT_START      1
#define TELJJY_EVENT_CONNECT    2
#define TELJJY_EVENT_DISCONNECT 3
#define TELJJY_EVENT_COMMAND    4
#define TELJJY_EVENT_LOGIN      5       /* Posted by the jjy_receive_telephone */
#define TELJJY_EVENT_PROMPT     6       /* Posted by the jjy_receive_telephone */
#define TELJJY_EVENT_DATA       7       /* Posted by the jjy_receive_telephone */
#define TELJJY_EVENT_ERROR      8       /* Posted by the jjy_receive_telephone */
#define TELJJY_EVENT_SILENT     9       /* Posted by the jjy_timer_telephone */
#define TELJJY_EVENT_TIMEOUT    10      /* Posted by the jjy_timer_telephone */

static  void    teljjy_control          ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;

static  int     teljjy_idle_ignore      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_idle_dialout     ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_dial_ignore      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_dial_login       ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_dial_disc        ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_ignore     ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_disc       ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_conn       ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_login      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_silent     ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_login_error      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_ignore      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_disc        ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_send        ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_data        ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_silent      ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_conn_error       ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_bye_ignore       ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_bye_disc         ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static  int     teljjy_bye_modem        ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;

static int ( *pTeljjyHandler [ ] [ 5 ] ) ( struct peer *, struct refclockproc *, struct jjyunit *) =
{                       /*STATE_IDLE           STATE_DAILOUT       STATE_LOGIN           STATE_CONNECT       STATE_BYE        */
/* NULL       */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* START      */        { teljjy_idle_dialout, teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* CONNECT    */        { teljjy_idle_ignore , teljjy_dial_login , teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* DISCONNECT */        { teljjy_idle_ignore , teljjy_dial_disc  , teljjy_login_disc  , teljjy_conn_disc  , teljjy_bye_disc   },
/* COMMAND    */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_modem  },
/* LOGIN      */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_login , teljjy_conn_error , teljjy_bye_ignore },
/* PROMPT     */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_conn  , teljjy_conn_send  , teljjy_bye_ignore },
/* DATA       */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_data  , teljjy_bye_ignore },
/* ERROR      */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_error , teljjy_conn_error , teljjy_bye_ignore },
/* SILENT     */        { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_silent, teljjy_conn_silent, teljjy_bye_ignore },
/* TIMEOUT    */        { teljjy_idle_ignore , teljjy_dial_disc  , teljjy_login_error , teljjy_conn_error , teljjy_bye_modem  }
} ;

static short iTeljjyNextState [ ] [ 5 ] =
{                       /*STATE_IDLE            STATE_DAILOUT         STATE_LOGIN           STATE_CONNECT         STATE_BYE         */
/* NULL       */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* START      */        { TELJJY_STATE_DAILOUT, TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* CONNECT    */        { TELJJY_STATE_IDLE   , TELJJY_STATE_LOGIN  , TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* DISCONNECT */        { TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE },
/* COMMAND    */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* LOGIN      */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* PROMPT     */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_CONNECT, TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* DATA       */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* ERROR      */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_BYE    , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* SILENT     */        { TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* TIMEOUT    */        { TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_BYE    , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  }
} ;

static short iTeljjyPostEvent [ ] [ 5 ] =
{                       /*STATE_IDLE         STATE_DAILOUT      STATE_LOGIN           STATE_CONNECT         STATE_BYE         */
/* NULL       */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* START      */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* CONNECT    */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* DISCONNECT */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* COMMAND    */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* LOGIN      */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* PROMPT     */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_PROMPT , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* DATA       */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* ERROR      */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* SILENT     */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* TIMEOUT    */        { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL }
} ;

static short iTeljjySilentTimeout [ 5 ] = { 0,   0, 10,  5,  0 } ;
static short iTeljjyStateTimeout  [ 5 ] = { 0, 120, 60, 60, 40 } ;

#define TELJJY_STAY_CLOCK_STATE         0
#define TELJJY_CHANGE_CLOCK_STATE       1

/* Command and replay */

#define TELJJY_REPLY_NONE       0
#define TELJJY_REPLY_4DATE      1
#define TELJJY_REPLY_TIME       2
#define TELJJY_REPLY_LEAPSEC    3
#define TELJJY_REPLY_LOOP       4
#define TELJJY_REPLY_PROMPT     5
#define TELJJY_REPLY_LOOPBACK   6
#define TELJJY_REPLY_COM        7

#define TELJJY_COMMAND_START_SKIP_LOOPBACK      7

static  struct
{
       const char      *command ;
       int     commandLength ;
       int     iEchobackReplyLength ;
       int     iExpectedReplyType   ;
       int     iExpectedReplyLength ;
} teljjy_command_sequence[] =
{
       { NULL, 0, 0, 0, 0 }, /* Idle */
       { "LOOP\r"   , 5, 4, TELJJY_REPLY_LOOP    , 0 }, /* Getting into loopback mode */
       { ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
       { ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
       { ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
       { ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
       { ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
       { "COM\r"    , 4, 3, TELJJY_REPLY_COM     , 0 }, /* Exit from loopback mode */
       /* TELJJY_COMMAND_START_SKIP_LOOPBACK */
       { "TIME\r"   , 5, 4, TELJJY_REPLY_TIME    , 6 },
       { "4DATE\r"  , 6, 5, TELJJY_REPLY_4DATE   , 8 },
       { "LEAPSEC\r", 8, 7, TELJJY_REPLY_LEAPSEC , 2 },
       { "TIME\r"   , 5, 4, TELJJY_REPLY_TIME    , 6 },
       { "BYE\r"    , 4, 3, TELJJY_REPLY_NONE    , 0 },
       /* End of command */
       { NULL, 0, 0, 0, 0 }
} ;

#define TELJJY_LOOPBACK_DELAY_THRESHOLD         700 /* Milli second */

#ifdef  DEBUG
#define DEBUG_TELJJY_PRINTF(sFunc)      { if ( debug ) { printf ( "refclock_jjy.c : %s : iClockState=%d iClockEvent=%d iTeljjySilentTimer=%d iTeljjyStateTimer=%d iClockCommandSeq=%d\n", sFunc, up->iClockState, up->iClockEvent, up->iTeljjySilentTimer, up->iTeljjyStateTimer, up->iClockCommandSeq ) ; } }
#else
#define DEBUG_TELJJY_PRINTF(sFunc)
#endif

/**************************************************************************************************/

static int
jjy_start_telephone ( int unit, struct peer *peer, struct jjyunit *up )
{

       char    sLog [ 192 ], sFirstThreeDigits [ 4 ] ;
       int     iNumberOfDigitsOfPhoneNumber, iCommaCount, iCommaPosition ;
       size_t  i ;
       size_t  iFirstThreeDigitsCount ;

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Telephone JJY" ) ;

       up->unittype  = UNITTYPE_TELEPHONE ;
       up->linespeed = SPEED232_TELEPHONE ;
       up->linediscipline = LDISC_RAW ;

       up->pRawBreak = teljjy_raw_break ;
       up->bWaitBreakString = TRUE ;

       up->bSkipCntrlCharOnly = TRUE ;

       up->iClockState = TELJJY_STATE_IDLE ;
       up->iClockEvent = TELJJY_EVENT_NULL ;

       /* Check the telephone number */

       if ( sys_phone[0] == NULL ) {
               msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf must be specified." ) ;
               up->bInitError = TRUE ;
               return 1 ;
       }

       if ( sys_phone[1] != NULL ) {
               msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be only one." ) ;
               up->bInitError = TRUE ;
               return 1 ;
       }

       iNumberOfDigitsOfPhoneNumber = iCommaCount = iCommaPosition = iFirstThreeDigitsCount = 0 ;
       for ( i = 0 ; i < strlen( sys_phone[0] ) ; i ++ ) {
               if ( isdigit( (u_char)sys_phone[0][i] ) ) {
                       if ( iFirstThreeDigitsCount < sizeof(sFirstThreeDigits)-1 ) {
                               sFirstThreeDigits[iFirstThreeDigitsCount++] = sys_phone[0][i] ;
                       }
                       iNumberOfDigitsOfPhoneNumber ++ ;
               } else if ( sys_phone[0][i] == ',' ) {
                       iCommaCount ++ ;
                       if ( iCommaCount > 1 ) {
                               msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be zero or one comma." ) ;
                               up->bInitError = TRUE ;
                               return 1 ;
                       }
                       iFirstThreeDigitsCount = 0 ;
                       iCommaPosition = i ;
               } else if ( sys_phone[0][i] != '-' ) {
                       msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be a number or a hyphen." ) ;
                       up->bInitError = TRUE ;
                       return 1 ;
               }
       }
       sFirstThreeDigits[iFirstThreeDigitsCount] = 0 ;

       if ( iCommaCount == 1 ) {
               if ( iCommaPosition != 1 || *sys_phone[0] != '0' ) {
                       msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : Getting an outside line should be '0,'." ) ;
                       up->bInitError = TRUE ;
                       return 1 ;
               }
       }

       if ( iNumberOfDigitsOfPhoneNumber - iCommaPosition < 6 || 10 < iNumberOfDigitsOfPhoneNumber - iCommaPosition ) {
               /* Too short or too long */
               msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone=%s : Number of digits should be 6 to 10.", sys_phone[0] ) ;
               up->bInitError = TRUE ;
               return 1 ;
       }

       if ( strncmp( sFirstThreeDigits + iCommaPosition, "00" , 2 ) == 0
         || strncmp( sFirstThreeDigits + iCommaPosition, "10" , 2 ) == 0
         || strncmp( sFirstThreeDigits + iCommaPosition, "11" , 2 ) == 0
         || strncmp( sFirstThreeDigits + iCommaPosition, "12" , 2 ) == 0
         || strncmp( sFirstThreeDigits + iCommaPosition, "171", 3 ) == 0
         || strncmp( sFirstThreeDigits + iCommaPosition, "177", 3 ) == 0
         || ( sFirstThreeDigits[0] == '0' &&  sFirstThreeDigits[2] == '0' ) ) {
               /* Not allowed because of emergency numbers or special service numbers */
               msyslog( LOG_ERR, "refclock_jjy.c : jjy_start_telephone : phone=%s : First 2 or 3 digits are not allowed.", sys_phone[0] ) ;
               up->bInitError = TRUE ;
               return 1 ;
       }

       snprintf( sLog, sizeof(sLog), "phone=%s", sys_phone[0] ) ;
       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

       if ( peer->minpoll < 8 ) {
               /* minpoll must be greater or equal to 8 ( 256 seconds = about 4 minutes ) */
               int oldminpoll = peer->minpoll ;
               peer->minpoll = 8 ;
               if ( peer->ppoll < peer->minpoll ) {
                       peer->ppoll = peer->minpoll ;
               }
               if ( peer->maxpoll < peer->minpoll ) {
                       peer->maxpoll = peer->minpoll ;
               }
               snprintf( sLog, sizeof(sLog), "minpoll=%d -> %d", oldminpoll, peer->minpoll ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;
       }

       return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_telephone ( struct recvbuf *rbufp )
{
#ifdef DEBUG
       static  const char      *sFunctionName = "jjy_receive_telephone" ;
#endif

       struct  peer         *peer;
       struct  refclockproc *pp ;
       struct  jjyunit      *up ;
       char    *pBuf ;
       int     iLen ;
       short   iPreviousModemState ;

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_TELJJY_PRINTF( sFunctionName ) ;

       if ( up->iClockState == TELJJY_STATE_IDLE
         || up->iClockState == TELJJY_STATE_DAILOUT
         || up->iClockState == TELJJY_STATE_BYE ) {

               iPreviousModemState = getModemState( up ) ;

               modem_receive ( rbufp ) ;

               if ( iPreviousModemState != up->iModemState ) {
                       /* Modem state is changed just now. */
                       if ( isModemStateDisconnect( up->iModemState ) ) {
                               up->iClockEvent = TELJJY_EVENT_DISCONNECT ;
                               teljjy_control ( peer, pp, up ) ;
                       } else if ( isModemStateConnect( up->iModemState ) ) {
                               up->iClockEvent = TELJJY_EVENT_CONNECT ;
                               teljjy_control ( peer, pp, up ) ;
                       }
               }

               return JJY_RECEIVE_WAIT ;

       }

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       up->iTeljjySilentTimer = 0 ;
       if      ( iLen == 7 && strncmp( pBuf, "Name ? ", 7 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_LOGIN  ; }
       else if ( iLen == 1 && strncmp( pBuf, ">"      , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_PROMPT ; }
       else if ( iLen >= 1 && strncmp( pBuf, "?"      , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_ERROR  ; }
       else                                                        { up->iClockEvent = TELJJY_EVENT_DATA   ; }

       teljjy_control ( peer, pp, up ) ;

       return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_telephone ( int unit, struct peer *peer )
{
#ifdef DEBUG
       static const char *sFunctionName = "jjy_poll_telephone" ;
#endif

       struct  refclockproc *pp ;
       struct  jjyunit      *up ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_TELJJY_PRINTF( sFunctionName ) ;

       if ( up->iClockState == TELJJY_STATE_IDLE ) {
               up->iRawBufLen = 0 ;
               up->iLineBufLen = 0 ;
               up->iTextBufLen = 0 ;
       }

       up->iClockEvent = TELJJY_EVENT_START ;
       teljjy_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static void
jjy_timer_telephone ( int unit, struct peer *peer )
{
#ifdef DEBUG
       static const char *sFunctionName = "jjy_timer_telephone" ;
#endif

       struct  refclockproc *pp ;
       struct  jjyunit      *up ;
       short   iPreviousModemState ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_TELJJY_PRINTF( sFunctionName ) ;

       if ( iTeljjySilentTimeout[up->iClockState] != 0 ) {
               up->iTeljjySilentTimer++ ;
               if ( iTeljjySilentTimeout[up->iClockState] <= up->iTeljjySilentTimer ) {
                       up->iClockEvent = TELJJY_EVENT_SILENT ;
                       teljjy_control ( peer, pp, up ) ;
               }
       }

       if ( iTeljjyStateTimeout[up->iClockState] != 0 ) {
               up->iTeljjyStateTimer++ ;
               if ( iTeljjyStateTimeout[up->iClockState] <= up->iTeljjyStateTimer ) {
                       up->iClockEvent = TELJJY_EVENT_TIMEOUT ;
                       teljjy_control ( peer, pp, up ) ;
               }
       }

       if ( isModemStateTimerOn( up ) ) {

               iPreviousModemState = getModemState( up ) ;

               modem_timer ( unit, peer ) ;

               if ( iPreviousModemState != up->iModemState ) {
                       /* Modem state is changed just now. */
                       if ( isModemStateDisconnect( up->iModemState ) ) {
                               up->iClockEvent = TELJJY_EVENT_DISCONNECT ;
                               teljjy_control ( peer, pp, up ) ;
                       } else if ( isModemStateConnect( up->iModemState ) ) {
                               up->iClockEvent = TELJJY_EVENT_CONNECT ;
                               teljjy_control ( peer, pp, up ) ;
                       }
               }

       }

}

/**************************************************************************************************/

static void
teljjy_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       int     i, rc ;
       short   iPostEvent = TELJJY_EVENT_NULL ;

       DEBUG_TELJJY_PRINTF( "teljjy_control" ) ;

       rc = (*pTeljjyHandler[up->iClockEvent][up->iClockState])( peer, pp, up ) ;

       if ( rc == TELJJY_CHANGE_CLOCK_STATE ) {
               iPostEvent = iTeljjyPostEvent[up->iClockEvent][up->iClockState] ;
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : teljjy_control : iClockState=%hd -> %hd  iPostEvent=%hd\n",
                               up->iClockState, iTeljjyNextState[up->iClockEvent][up->iClockState], iPostEvent ) ;
               }
#endif
               up->iTeljjySilentTimer = 0 ;
               if ( up->iClockState != iTeljjyNextState[up->iClockEvent][up->iClockState] ) {
                       /* Telephone JJY state is changing now */
                       up->iTeljjyStateTimer = 0 ;
                       up->bLineError = FALSE ;
                       up->iClockCommandSeq = 0 ;
                       up->iTimestampCount = 0 ;
                       up->iLoopbackCount = 0 ;
                       for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
                               up->bLoopbackTimeout[i] = FALSE ;
                       }
                       if (iTeljjyNextState[up->iClockEvent][up->iClockState] == TELJJY_STATE_IDLE ) {
                               /* Telephone JJY state is changing to IDLE just now */
                               up->iProcessState = JJY_PROCESS_STATE_DONE ;
                       }
               }
               up->iClockState = iTeljjyNextState[up->iClockEvent][up->iClockState] ;

       }

       if ( iPostEvent != TELJJY_EVENT_NULL ) {
               up->iClockEvent = iPostEvent ;
               teljjy_control ( peer, pp, up ) ;
       }

       up->iClockEvent = TELJJY_EVENT_NULL ;

}

/**************************************************************************************************/

static void
teljjy_setDelay ( struct peer *peer, struct jjyunit *up )
{

       char    sLog [ 60 ] ;
       int     milliSecond, microSecond ;

       gettimeofday( &(up->delayTime[up->iLoopbackCount]), NULL ) ;

       up->delayTime[up->iLoopbackCount].tv_sec  -= up->sendTime[up->iLoopbackCount].tv_sec ;
       up->delayTime[up->iLoopbackCount].tv_usec -= up->sendTime[up->iLoopbackCount].tv_usec ;
       if ( up->delayTime[up->iLoopbackCount].tv_usec < 0 ) {
               up->delayTime[up->iLoopbackCount].tv_sec -- ;
               up->delayTime[up->iLoopbackCount].tv_usec += 1000000 ;
       }

       milliSecond  = up->delayTime[up->iLoopbackCount].tv_usec / 1000 ;
       microSecond  = up->delayTime[up->iLoopbackCount].tv_usec - milliSecond * 1000 ;
       milliSecond += up->delayTime[up->iLoopbackCount].tv_sec * 1000 ;

       snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY,
                 milliSecond, microSecond ) ;

       if ( milliSecond > TELJJY_LOOPBACK_DELAY_THRESHOLD ) {
               /* Delay > 700 mS */
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;
       } else {
               /* Delay <= 700 mS */
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
       }

}

/**************************************************************************************************/

static int
teljjy_getDelay ( struct peer *peer, struct jjyunit *up )
{

       struct  timeval maxTime, minTime, averTime ;
       int     i ;
       int     minIndex = 0, maxIndex = 0, iAverCount = 0 ;
       int     iThresholdSecond, iThresholdMicroSecond ;
       int     iPercent ;

       minTime.tv_sec = minTime.tv_usec = 0 ;
       maxTime.tv_sec = maxTime.tv_usec = 0 ;

       iThresholdSecond = TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ;
       iThresholdMicroSecond = ( TELJJY_LOOPBACK_DELAY_THRESHOLD - ( TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ) * 1000 ) * 1000 ;

       up->iLoopbackValidCount = 0 ;

       for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) {
               if ( up->bLoopbackTimeout[i]
                 || up->delayTime[i].tv_sec  > iThresholdSecond
               || ( up->delayTime[i].tv_sec == iThresholdSecond
                 && up->delayTime[i].tv_usec > iThresholdMicroSecond ) ) {
                       continue ;
               }
               if ( up->iLoopbackValidCount == 0 ) {
                       minTime.tv_sec  = up->delayTime[i].tv_sec  ;
                       minTime.tv_usec = up->delayTime[i].tv_usec ;
                       maxTime.tv_sec  = up->delayTime[i].tv_sec  ;
                       maxTime.tv_usec = up->delayTime[i].tv_usec ;
                       minIndex = maxIndex = i ;
               } else if ( minTime.tv_sec  > up->delayTime[i].tv_sec
                      || ( minTime.tv_sec == up->delayTime[i].tv_sec
                        && minTime.tv_usec > up->delayTime[i].tv_usec ) ) {
                       minTime.tv_sec  = up->delayTime[i].tv_sec  ;
                       minTime.tv_usec = up->delayTime[i].tv_usec ;
                       minIndex = i ;
               } else if ( maxTime.tv_sec  < up->delayTime[i].tv_sec
                      || ( maxTime.tv_sec == up->delayTime[i].tv_sec
                        && maxTime.tv_usec < up->delayTime[i].tv_usec ) ) {
                       maxTime.tv_sec  = up->delayTime[i].tv_sec  ;
                       maxTime.tv_usec = up->delayTime[i].tv_usec ;
                       maxIndex = i ;
               }
               up->iLoopbackValidCount ++ ;
       }

       if ( up->iLoopbackValidCount < 2 ) {
               return -1 ;
       }

       averTime.tv_usec = 0;

       for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) {
               if ( up->bLoopbackTimeout[i]
                 || up->delayTime[i].tv_sec  > iThresholdSecond
               || ( up->delayTime[i].tv_sec == iThresholdSecond
                 && up->delayTime[i].tv_usec > iThresholdMicroSecond ) ) {
                       continue ;
               }
               if ( up->iLoopbackValidCount >= 3 && i == maxIndex ) {
                       continue ;
               }
               if ( up->iLoopbackValidCount >= 4 && i == minIndex ) {
                       continue ;
               }
               averTime.tv_usec += up->delayTime[i].tv_usec ;
               iAverCount ++ ;
       }

       if ( iAverCount == 0 ) {
               /* This is never happened. */
               /* Previous for-if-for blocks assure iAverCount > 0. */
               /* This code avoids a claim by the coverity scan tool. */
               return -1 ;
       }

       /* mode 101 = 1%, mode 150 = 50%, mode 180 = 80% */

       iPercent = ( peer->ttl - 100 ) ;

       /* Average delay time in milli second */

       return ( ( averTime.tv_usec / iAverCount ) * iPercent ) / 100000 ;

}

/******************************/
static int
teljjy_idle_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_idle_ignore" ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_idle_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_idle_dialout" ) ;

       modem_connect ( peer->refclkunit, peer ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_dial_ignore" ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_dial_login" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_dial_disc" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_login_ignore" ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_login_disc" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_conn ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       int     i ;

       DEBUG_TELJJY_PRINTF( "teljjy_login_conn" ) ;

       up->bLineError = FALSE ;
       up->iClockCommandSeq = 0 ;
       up->iTimestampCount = 0 ;
       up->iLoopbackCount = 0 ;
       for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
               up->bLoopbackTimeout[i] = FALSE ;
       }

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;
       int             iCmdLen ;

       DEBUG_TELJJY_PRINTF( "teljjy_login_login" ) ;

       /* Send a guest user ID */
       pCmd = "TJJY\r" ;

       /* Send login ID */
       iCmdLen = strlen( pCmd ) ;
       if ( write( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_login_silent" ) ;

       if ( write( pp->io.fd, "\r", 1 ) != 1 ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\r" ) ;

       up->iTeljjySilentTimer = 0 ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_login_error" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_conn_ignore" ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_conn_disc" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_send ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;
       int             i, iLen, iNextClockState ;
       char    sLog [ 120 ] ;

       DEBUG_TELJJY_PRINTF( "teljjy_conn_send" ) ;

       if ( up->iClockCommandSeq > 0
         && teljjy_command_sequence[up->iClockCommandSeq].command == NULL ) {
               /* Command sequence has been completed */
               return TELJJY_CHANGE_CLOCK_STATE ;
       }

       if ( up->iClockCommandSeq == 0 && peer->ttl == 100 ) {
               /* Skip loopback */

               up->iClockCommandSeq = TELJJY_COMMAND_START_SKIP_LOOPBACK ;

       } else if ( up->iClockCommandSeq == 0 && peer->ttl != 100 ) {
               /* Loopback start */

               up->iLoopbackCount = 0 ;
               for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
                       up->bLoopbackTimeout[i] = FALSE ;
               }

       } else if ( up->iClockCommandSeq > 0 && peer->ttl != 100
                && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK
                && up->iLoopbackCount < MAX_LOOPBACK ) {
               /* Loopback character comes */
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : teljjy_conn_send : iClockCommandSeq=%d iLoopbackCount=%d\n",
                                up->iClockCommandSeq, up->iLoopbackCount ) ;
               }
#endif

               teljjy_setDelay( peer, up ) ;

               up->iLoopbackCount ++ ;

       }

       up->iClockCommandSeq++ ;

       pCmd = teljjy_command_sequence[up->iClockCommandSeq].command ;
       iLen = teljjy_command_sequence[up->iClockCommandSeq].commandLength ;

       if ( pCmd != NULL ) {

               if ( write( pp->io.fd, pCmd, iLen ) != iLen ) {
                       refclock_report( peer, CEVNT_FAULT ) ;
               }

               if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
                       /* Loopback character and timestamp */
                       if ( up->iLoopbackCount < MAX_LOOPBACK ) {
                               gettimeofday( &(up->sendTime[up->iLoopbackCount]), NULL ) ;
                               up->bLoopbackMode = TRUE ;
                       } else {
                               /* This else-block is never come. */
                               /* This code avoid wrong report of the coverity static analysis scan tool. */
                               snprintf( sLog, sizeof(sLog)-1, "refclock_jjy.c ; teljjy_conn_send ; iClockCommandSeq=%d iLoopbackCount=%d MAX_LOOPBACK=%d",
                                         up->iClockCommandSeq, up->iLoopbackCount, MAX_LOOPBACK ) ;
                               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_BUG, sLog ) ;
                               msyslog ( LOG_ERR, "%s", sLog ) ;
                               up->bLoopbackMode = FALSE ;
                       }
               } else {
                       /* Regular command */
                       up->bLoopbackMode = FALSE ;
               }

               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

               if ( teljjy_command_sequence[up->iClockCommandSeq+1].command == NULL ) {
                       /* Last command of the command sequence */
                       iNextClockState = TELJJY_CHANGE_CLOCK_STATE ;
               } else {
                       /* More commands to be issued */
                       iNextClockState = TELJJY_STAY_CLOCK_STATE ;
               }

       } else {

               iNextClockState = TELJJY_CHANGE_CLOCK_STATE ;

       }

       return iNextClockState ;

}

/******************************/
static int
teljjy_conn_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       char    *pBuf ;
       int     iLen, rc ;
       char    sLog [ MAX_LOGTEXT ] ;
       char    bAdjustment ;


       DEBUG_TELJJY_PRINTF( "teljjy_conn_data" ) ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen
         && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK
         && up->sTextBuf[0] == *(teljjy_command_sequence[up->iClockCommandSeq].command)
         && up->iLoopbackCount < MAX_LOOPBACK ) {
               /* Loopback */

               teljjy_setDelay( peer, up ) ;

               up->iLoopbackCount ++ ;

       } else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen
           && strncmp( pBuf, teljjy_command_sequence[up->iClockCommandSeq].command, iLen ) == 0 ) {
               /* Maybe echoback */

               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, JJY_CLOCKSTATS_MESSAGE_ECHOBACK ) ;

       } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
                && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_4DATE ) {
               /* 4DATE<CR> -> YYYYMMDD<CR> */

               rc = sscanf ( pBuf, "%4d%2d%2d", &up->year, &up->month, &up->day ) ;

               if ( rc != 3 || up->year < 2000 || 2099 <= up->year
                 || up->month < 1 || 12 < up->month || up->day < 1 || 31 < up->day ) {
                       /* Invalid date */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
                                 rc, up->year, up->month, up->day ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
               }

       } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
                && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LEAPSEC
                && ( strncmp( pBuf, " 0", 2 ) == 0 || strncmp( pBuf, "+1", 2 ) == 0 || strncmp( pBuf, "-1", 2 ) == 0 ) ) {
               /* LEAPSEC<CR> -> XX<CR> ( One of <SP>0, +1, -1 ) */

               rc = sscanf ( pBuf, "%2d", &up->leapsecond ) ;

               if ( rc != 1 || up->leapsecond < -1 || 1 < up->leapsecond ) {
                       /* Invalid leap second */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP,
                                 pBuf ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
               }

       } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
                && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_TIME ) {
               /* TIME<CR> -> HHMMSS<CR> ( 3 times on second ) */

               rc = sscanf ( pBuf, "%2d%2d%2d", &up->hour, &up->minute, &up->second ) ;

               if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
                       /* Invalid time */
                       snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
                                 rc, up->hour, up->minute, up->second ) ;
                       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                       up->bLineError = TRUE ;
               }
               up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

               up->iTimestampCount++ ;

               if ( up->iTimestampCount == 6 && ! up->bLineError ) {
#if DEBUG
                       printf( "refclock_jjy.c : teljjy_conn_data : bLineError=%d iTimestamp=%d, %d, %d\n",
                               up->bLineError,
                               up->iTimestamp[3], up->iTimestamp[4], up->iTimestamp[5] ) ;
#endif
                       bAdjustment = TRUE ;

                       if ( peer->ttl == 100 ) {
                               /* mode=100 */
                               up->msecond = 0 ;
                       } else {
                               /* mode=101 to 110 */
                               up->msecond = teljjy_getDelay( peer, up ) ;
                               if (up->msecond < 0 ) {
                                       up->msecond = 0 ;
                                       bAdjustment = FALSE ;
                               }
                       }

                       if ( ( up->iTimestamp[3] - 15 ) <= up->iTimestamp[2]
                         &&   up->iTimestamp[2]        <= up->iTimestamp[3]
                         && ( up->iTimestamp[3] +  1 ) == up->iTimestamp[4]
                         && ( up->iTimestamp[4] +  1 ) == up->iTimestamp[5] ) {
                               /* Non over midnight */

                               jjy_synctime( peer, pp, up ) ;

                               if ( peer->ttl != 100 ) {
                                       if ( bAdjustment ) {
                                               snprintf( sLog, sizeof(sLog),
                                                         JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST,
                                                         up->msecond, up->iLoopbackValidCount, MAX_LOOPBACK ) ;
                                               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
                                       } else {
                                               snprintf( sLog, sizeof(sLog),
                                                         JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST,
                                                          up->iLoopbackValidCount, MAX_LOOPBACK ) ;
                                               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
                                       }
                               }

                       }
               }

       } else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength != iLen
                && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
               /* Loopback noise ( Unexpected replay ) */

               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;

       } else {

               up->bLineError = TRUE ;

               snprintf( sLog, sizeof(sLog)-1, JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
                         pBuf ) ;
               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;

       }

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;

       DEBUG_TELJJY_PRINTF( "teljjy_conn_silent" ) ;

       if ( up->iClockCommandSeq >= 1
         && up->iClockCommandSeq < TELJJY_COMMAND_START_SKIP_LOOPBACK ) {
               /* Loopback */
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : teljjy_conn_silent : call teljjy_conn_send\n" ) ;
               }
#endif
               if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
                       up->bLoopbackTimeout[up->iLoopbackCount] = TRUE ;
               }
               up->iTeljjySilentTimer = 0 ;
               return teljjy_conn_send( peer, pp, up ) ;
       } else {
               pCmd = "\r" ;
       }

       if ( write( pp->io.fd, pCmd, 1 ) != 1 ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       up->iTeljjySilentTimer = 0 ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_conn_error" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_bye_ignore" ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_bye_disc" ) ;

       return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_modem ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_TELJJY_PRINTF( "teljjy_bye_modem" ) ;

       modem_disconnect ( peer->refclkunit, peer ) ;

       return TELJJY_STAY_CLOCK_STATE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    Modem control finite state machine                                                      ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/

/* struct jjyunit.iModemState */

#define MODEM_STATE_DISCONNECT          0
#define MODEM_STATE_INITIALIZE          1
#define MODEM_STATE_DAILING             2
#define MODEM_STATE_CONNECT             3
#define MODEM_STATE_ESCAPE              4

/* struct jjyunit.iModemEvent */

#define MODEM_EVENT_NULL                0
#define MODEM_EVENT_INITIALIZE          1
#define MODEM_EVENT_DIALOUT             2
#define MODEM_EVENT_DISCONNECT          3
#define MODEM_EVENT_RESP_OK             4
#define MODEM_EVENT_RESP_CONNECT        5
#define MODEM_EVENT_RESP_RING           6
#define MODEM_EVENT_RESP_NO_CARRIER     7
#define MODEM_EVENT_RESP_ERROR          8
#define MODEM_EVENT_RESP_CONNECT_X      9
#define MODEM_EVENT_RESP_NO_DAILTONE    10
#define MODEM_EVENT_RESP_BUSY           11
#define MODEM_EVENT_RESP_NO_ANSWER      12
#define MODEM_EVENT_RESP_UNKNOWN        13
#define MODEM_EVENT_SILENT              14
#define MODEM_EVENT_TIMEOUT             15

/* Function prototypes */

static  void    modem_control           ( struct peer *, struct refclockproc *, struct jjyunit * ) ;

static  int     modem_disc_ignore       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_disc_init         ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_init_ignore       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_init_start        ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_init_disc         ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_init_resp00       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_init_resp04       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_dial_ignore       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_dial_dialout      ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_dial_escape       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_dial_connect      ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_dial_disc         ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_conn_ignore       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_conn_escape       ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_esc_ignore        ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_esc_escape        ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_esc_data          ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_esc_silent        ( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static  int     modem_esc_disc          ( struct peer *, struct refclockproc *, struct jjyunit * ) ;

static int ( *pModemHandler [ ] [ 5 ] ) ( struct peer *, struct refclockproc *, struct jjyunit * ) =
{                               /*STATE_DISCONNECT   STATE_INITIALIZE   STATE_DAILING       STATE_CONNECT      STATE_ESCAPE     */
/* NULL                 */      { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_ignore },
/* INITIALIZE           */      { modem_disc_init  , modem_init_start , modem_dial_ignore , modem_conn_ignore, modem_esc_ignore },
/* DIALOUT              */      { modem_disc_ignore, modem_init_ignore, modem_dial_dialout, modem_conn_ignore, modem_esc_ignore },
/* DISCONNECT           */      { modem_disc_ignore, modem_init_disc  , modem_dial_escape , modem_conn_escape, modem_esc_escape },
/* RESP: 0: OK          */      { modem_disc_ignore, modem_init_resp00, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* RESP: 1: CONNECT     */      { modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data   },
/* RESP: 2: RING        */      { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* RESP: 3: NO CARRIER  */      { modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 4: ERROR       */      { modem_disc_ignore, modem_init_resp04, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 5: CONNECT     */      { modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data   },
/* RESP: 6: NO DAILTONE */      { modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 7: BUSY        */      { modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 8: NO ANSWER   */      { modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 9: UNKNOWN     */      { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* SILENT               */      { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_silent },
/* TIMEOUT              */      { modem_disc_ignore, modem_init_disc  , modem_dial_escape , modem_conn_escape, modem_esc_disc   }
} ;

static short iModemNextState [ ] [ 5 ] =
{                               /*STATE_DISCONNECT        STATE_INITIALIZE        STATE_DAILING        STATE_CONNECT        STATE_ESCAPE           */
/* NULL                 */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* INITIALIZE           */      { MODEM_STATE_INITIALIZE, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* DIALOUT              */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* DISCONNECT           */      { MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE    , MODEM_STATE_ESCAPE , MODEM_STATE_ESCAPE     },
/* RESP: 0: OK          */      { MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING   , MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 1: CONNECT     */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 2: RING        */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 3: NO CARRIER  */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 4: ERROR       */      { MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING   , MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 5: CONNECT X   */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 6: NO DAILTONE */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 7: BUSY        */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 8: NO ANSWER   */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 9: UNKNOWN     */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* SILENT               */      { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_DISCONNECT },
/* TIMEOUT              */      { MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE    , MODEM_STATE_ESCAPE , MODEM_STATE_DISCONNECT }
} ;

static short iModemPostEvent [ ] [ 5 ] =
{                               /*STATE_DISCONNECT        STATE_INITIALIZE     STATE_DAILING           STATE_CONNECT           STATE_ESCAPE     */
/* NULL                 */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* INITIALIZE           */      { MODEM_EVENT_INITIALIZE, MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* DIALOUT              */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* DISCONNECT           */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL },
/* RESP: 0: OK          */      { MODEM_EVENT_NULL      , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 1: CONNECT     */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 2: RING        */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 3: NO CARRIER  */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 4: ERROR       */      { MODEM_EVENT_NULL      , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 5: CONNECT X   */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 6: NO DAILTONE */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 7: BUSY        */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 8: NO ANSWER   */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 9: UNKNOWN     */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* SILENT               */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* TIMEOUT              */      { MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL }
} ;

static short iModemSilentTimeout [ 5 ] = { 0,  0,  0, 0,  5 } ;
static short iModemStateTimeout  [ 5 ] = { 0, 20, 90, 0, 20 } ;

#define STAY_MODEM_STATE        0
#define CHANGE_MODEM_STATE      1

#ifdef  DEBUG
#define DEBUG_MODEM_PRINTF(sFunc)       { if ( debug ) { printf ( "refclock_jjy.c : %s : iModemState=%d iModemEvent=%d iModemSilentTimer=%d iModemStateTimer=%d\n", sFunc, up->iModemState, up->iModemEvent, up->iModemSilentTimer, up->iModemStateTimer ) ; } }
#else
#define DEBUG_MODEM_PRINTF(sFunc)
#endif

/**************************************************************************************************/

static short
getModemState ( struct jjyunit *up )
{
       return up->iModemState ;
}

/**************************************************************************************************/

static int
isModemStateConnect ( short iCheckState )
{
       return ( iCheckState == MODEM_STATE_CONNECT ) ;
}

/**************************************************************************************************/

static int
isModemStateDisconnect ( short iCheckState )
{
       return ( iCheckState == MODEM_STATE_DISCONNECT ) ;
}

/**************************************************************************************************/

static int
isModemStateTimerOn ( struct jjyunit *up )
{
       return ( iModemSilentTimeout[up->iModemState] != 0 || iModemStateTimeout[up->iModemState] != 0 ) ;
}

/**************************************************************************************************/

static void
modem_connect ( int unit, struct peer *peer )
{
       struct  refclockproc    *pp;
       struct  jjyunit         *up;

       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_MODEM_PRINTF( "modem_connect" ) ;

       up->iModemEvent = MODEM_EVENT_INITIALIZE ;

       modem_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static void
modem_disconnect ( int unit, struct peer *peer )
{
       struct  refclockproc    *pp;
       struct  jjyunit         *up;

       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_MODEM_PRINTF( "modem_disconnect" ) ;

       up->iModemEvent = MODEM_EVENT_DISCONNECT ;

       modem_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static int
modem_receive ( struct recvbuf *rbufp )
{

       struct  peer            *peer;
       struct  jjyunit         *up;
       struct  refclockproc    *pp;
       char    *pBuf ;
       size_t  iLen ;

#ifdef DEBUG
       static const char *sFunctionName = "modem_receive" ;
#endif

       peer = rbufp->recv_peer ;
       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_MODEM_PRINTF( sFunctionName ) ;

       if ( up->linediscipline == LDISC_RAW ) {
               pBuf = up->sTextBuf ;
               iLen = up->iTextBufLen ;
       } else {
               pBuf = pp->a_lastcode ;
               iLen = pp->lencode ;
       }

       if      ( iLen ==  2 && strncmp( pBuf, "OK"         ,  2 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_OK          ; }
       else if ( iLen ==  7 && strncmp( pBuf, "CONNECT"    ,  7 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT     ; }
       else if ( iLen ==  4 && strncmp( pBuf, "RING"       ,  4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_RING        ; }
       else if ( iLen == 10 && strncmp( pBuf, "NO CARRIER" , 10 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_CARRIER  ; }
       else if ( iLen ==  5 && strncmp( pBuf, "ERROR"      ,  5 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_ERROR       ; }
       else if ( iLen >=  8 && strncmp( pBuf, "CONNECT "   ,  8 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT_X   ; }
       else if ( iLen == 11 && strncmp( pBuf, "NO DAILTONE", 11 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_DAILTONE ; }
       else if ( iLen ==  4 && strncmp( pBuf, "BUSY"       ,  4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_BUSY        ; }
       else if ( iLen ==  9 && strncmp( pBuf, "NO ANSWER"  ,  9 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_ANSWER   ; }
       else                                                              { up->iModemEvent = MODEM_EVENT_RESP_UNKNOWN     ; }

#ifdef DEBUG
       if ( debug ) {
               char    sResp [ 40 ] ;
               size_t  iCopyLen ;
               iCopyLen = ( iLen <= sizeof(sResp)-1 ? iLen : sizeof(sResp)-1 ) ;
               strncpy( sResp, pBuf, iLen <= sizeof(sResp)-1 ? iLen : sizeof(sResp)-1 ) ;
               sResp[iCopyLen] = 0 ;
               printf ( "refclock_jjy.c : modem_receive : iLen=%zu pBuf=[%s] iModemEvent=%d\n", iCopyLen, sResp, up->iModemEvent ) ;
       }
#endif
       modem_control ( peer, pp, up ) ;

       return 0 ;

}

/**************************************************************************************************/

static void
modem_timer ( int unit, struct peer *peer )
{

       struct  refclockproc *pp ;
       struct  jjyunit      *up ;

       pp = peer->procptr ;
       up = pp->unitptr ;

       DEBUG_MODEM_PRINTF( "modem_timer" ) ;

       if ( iModemSilentTimeout[up->iModemState] != 0 ) {
               up->iModemSilentTimer++ ;
               if ( iModemSilentTimeout[up->iModemState] <= up->iModemSilentTimer ) {
                       up->iModemEvent = MODEM_EVENT_SILENT ;
                       modem_control ( peer, pp, up ) ;
               }
       }

       if ( iModemStateTimeout[up->iModemState] != 0 ) {
               up->iModemStateTimer++ ;
               if ( iModemStateTimeout[up->iModemState] <= up->iModemStateTimer ) {
                       up->iModemEvent = MODEM_EVENT_TIMEOUT ;
                       modem_control ( peer, pp, up ) ;
               }
       }

}

/**************************************************************************************************/

static void
modem_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       int     rc ;
       short   iPostEvent = MODEM_EVENT_NULL ;

       DEBUG_MODEM_PRINTF( "modem_control" ) ;

       rc = (*pModemHandler[up->iModemEvent][up->iModemState])( peer, pp, up ) ;

       if ( rc == CHANGE_MODEM_STATE ) {
               iPostEvent = iModemPostEvent[up->iModemEvent][up->iModemState] ;
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : modem_control : iModemState=%d -> %d  iPostEvent=%d\n",
                                up->iModemState, iModemNextState[up->iModemEvent][up->iModemState], iPostEvent ) ;
               }
#endif

               if ( up->iModemState != iModemNextState[up->iModemEvent][up->iModemState] ) {
                       up->iModemSilentCount = 0 ;
                       up->iModemStateTimer = 0 ;
                       up->iModemCommandSeq = 0 ;
               }

               up->iModemState = iModemNextState[up->iModemEvent][up->iModemState] ;
       }

       if ( iPostEvent != MODEM_EVENT_NULL ) {
               up->iModemEvent = iPostEvent ;
               modem_control ( peer, pp, up ) ;
       }

       up->iModemEvent = MODEM_EVENT_NULL ;

}

/******************************/
static int
modem_disc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_disc_ignore" ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_disc_init ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_disc_init" ) ;

       return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_init_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_init_ignore" ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_init_start ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_init_start" ) ;

       up->iModemCommandSeq = 0 ;

#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : modem_init_start : call modem_init_resp00\n" ) ;
       }
#endif

       return modem_init_resp00( peer, pp, up ) ;

}

/******************************/
static int
modem_init_resp00 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;
       char            cBuf [ 46 ] ;
       int             iCmdLen ;
       int             iErrorCorrection, iSpeakerSwitch, iSpeakerVolume ;
       int             iNextModemState = STAY_MODEM_STATE ;

       DEBUG_MODEM_PRINTF( "modem_init_resp00" ) ;

       up->iModemCommandSeq++ ;

       switch ( up->iModemCommandSeq ) {

       case 1 :
               /* En = Echoback      0:Off      1:On   */
               /* Qn = Result codes  0:On       1:Off  */
               /* Vn = Result codes  0:Numeric  1:Text */
               pCmd = "ATE0Q0V1\r\n" ;
               break ;

       case 2 :
               /* Mn = Speaker switch  0:Off  1:On until remote carrier detected  2:On */
               if ( ( pp->sloppyclockflag & CLK_FLAG3 ) == 0 ) {
                       /* fudge 127.127.40.n flag3 0 */
                       iSpeakerSwitch = 0 ;
               } else {
                       /* fudge 127.127.40.n flag3 1 */
                       iSpeakerSwitch = 2 ;
               }

               /* Ln = Speaker volume  0:Very low  1:Low  2:Middle  3:High */
               if ( ( pp->sloppyclockflag & CLK_FLAG4 ) == 0 ) {
                       /* fudge 127.127.40.n flag4 0 */
                       iSpeakerVolume = 1 ;
               } else {
                       /* fudge 127.127.40.n flag4 1 */
                       iSpeakerVolume = 2 ;
               }

               pCmd = cBuf ;
               snprintf( cBuf, sizeof(cBuf), "ATM%dL%d\r\n", iSpeakerSwitch, iSpeakerVolume ) ;
               break ;

       case 3 :
               /* &Kn = Flow control  4:XON/XOFF */
               pCmd = "AT&K4\r\n" ;
               break ;

       case 4 :
               /* +MS = Protocol  V22B:1200,2400bps�iV.22bis) */
               pCmd = "AT+MS=V22B\r\n" ;
               break ;

       case 5 :
               /* %Cn = Data compression  0:No data compression */
               pCmd = "AT%C0\r\n" ;
               break ;

       case 6 :
               /* \Nn = Error correction  0:Normal mode  1:Direct mode  2:V42,MNP  3:V42,MNP,Normal */
               if ( ( pp->sloppyclockflag & CLK_FLAG2 ) == 0 ) {
                       /* fudge 127.127.40.n flag2 0 */
                       iErrorCorrection = 0 ;
               } else {
                       /* fudge 127.127.40.n flag2 1 */
                       iErrorCorrection = 3 ;
               }

               pCmd = cBuf ;
               snprintf( cBuf, sizeof(cBuf), "AT\\N%d\r\n", iErrorCorrection ) ;
               break ;

       case 7 :
               /* Hn = Hook  0:Hook-On ( Disconnect )  1:Hook-Off ( Connect ) */
               pCmd = "ATH1\r\n" ;
               break ;

       case 8 :
               /* Initialize completion */
               pCmd = NULL ;
               iNextModemState = CHANGE_MODEM_STATE ;
               break ;

       default :
               pCmd = NULL ;
               break ;

       }

       if ( pCmd != NULL ) {

               iCmdLen = strlen( pCmd ) ;
               if ( write( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
                       refclock_report( peer, CEVNT_FAULT ) ;
               }

               jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       }

       return iNextModemState ;

}

/******************************/
static int
modem_init_resp04 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_init_resp04" ) ;

       return modem_init_resp00( peer, pp, up ) ;

}

/******************************/
static int
modem_init_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_init_disc" ) ;
#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : modem_init_disc : call modem_esc_disc\n" ) ;
       }
#endif

       return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_dial_ignore" ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_dial_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       char    sCmd [ 46 ] ;
       int     iCmdLen ;
       char    cToneOrPulse ;

       DEBUG_MODEM_PRINTF( "modem_dial_dialout" ) ;

       /* Tone or Pulse */
       if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
               /* fudge 127.127.40.n flag1 0 */
               cToneOrPulse = 'T' ;
       } else {
               /* fudge 127.127.40.n flag1 1 */
               cToneOrPulse = 'P' ;
       }

       /* Connect ( Dial number ) */
       snprintf( sCmd, sizeof(sCmd), "ATDW%c%s\r\n", cToneOrPulse, *sys_phone ) ;

       /* Send command */
       iCmdLen = strlen( sCmd ) ;
       if ( write( pp->io.fd, sCmd, iCmdLen ) != iCmdLen ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_dial_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_dial_escape" ) ;
#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : modem_dial_escape : call modem_conn_escape\n" ) ;
       }
#endif

       return modem_conn_escape( peer, pp, up ) ;

}

/******************************/
static int
modem_dial_connect ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_dial_connect" ) ;

       return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_dial_disc" ) ;
#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : modem_dial_disc : call modem_esc_disc\n" ) ;
       }
#endif

       modem_esc_disc( peer, pp, up ) ;

       return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_conn_ignore" ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_conn_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_conn_escape" ) ;

       return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_esc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_esc_ignore" ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;
       int             iCmdLen ;

       DEBUG_MODEM_PRINTF( "modem_esc_escape" ) ;

       /* Escape command ( Go to command mode ) */
       pCmd = "+++" ;

       /* Send command */
       iCmdLen = strlen( pCmd ) ;
       if ( write( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_esc_data" ) ;

       up->iModemSilentTimer = 0 ;

       return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       DEBUG_MODEM_PRINTF( "modem_esc_silent" ) ;

       up->iModemSilentCount ++ ;

       if ( up->iModemSilentCount < iModemStateTimeout[up->iModemState] / iModemSilentTimeout[up->iModemState] ) {
#ifdef DEBUG
               if ( debug ) {
                       printf( "refclock_jjy.c : modem_esc_silent : call modem_esc_escape\n" ) ;
               }
#endif
               modem_esc_escape( peer, pp, up ) ;
               up->iModemSilentTimer = 0 ;
               return STAY_MODEM_STATE ;
       }

#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : modem_esc_silent : call modem_esc_disc\n" ) ;
       }
#endif
       return modem_esc_disc( peer, pp, up ) ;

}
/******************************/
static int
modem_esc_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

       const char *    pCmd ;
       int             iCmdLen ;

       DEBUG_MODEM_PRINTF( "modem_esc_disc" ) ;

       /* Disconnect */
       pCmd = "ATH0\r\n" ;

       /* Send command */
       iCmdLen = strlen( pCmd ) ;
       if ( write( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
               refclock_report( peer, CEVNT_FAULT ) ;
       }

       jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

       return CHANGE_MODEM_STATE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    jjy_write_clockstats                                                                    ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/

static void
jjy_write_clockstats ( struct peer *peer, int iMark, const char *pData )
{

       char            sLog [ 100 ] ;
       const char *    pMark ;
       int             iMarkLen, iDataLen ;

       switch ( iMark ) {
       case JJY_CLOCKSTATS_MARK_JJY :
               pMark = "JJY " ;
               break ;
       case JJY_CLOCKSTATS_MARK_SEND :
               pMark = "--> " ;
               break ;
       case JJY_CLOCKSTATS_MARK_RECEIVE :
               pMark = "<-- " ;
               break ;
       case JJY_CLOCKSTATS_MARK_INFORMATION :
               pMark = "--- " ;
               break ;
       case JJY_CLOCKSTATS_MARK_ATTENTION :
               pMark = "=== " ;
               break ;
       case JJY_CLOCKSTATS_MARK_WARNING :
               pMark = "-W- " ;
               break ;
       case JJY_CLOCKSTATS_MARK_ERROR :
               pMark = "-X- " ;
               break ;
       case JJY_CLOCKSTATS_MARK_BUG :
               pMark = "!!! " ;
               break ;
       default :
               pMark = "" ;
               break ;
       }

       iDataLen = strlen( pData ) ;
       iMarkLen = strlen( pMark ) ;
       strcpy( sLog, pMark ) ; /* Harmless because of enough length */
       printableString( sLog+iMarkLen, sizeof(sLog)-iMarkLen, pData, iDataLen ) ;

#ifdef DEBUG
       if ( debug ) {
               printf( "refclock_jjy.c : clockstats : %s\n", sLog ) ;
       }
#endif
       record_clock_stats( &peer->srcadr, sLog ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##                                                                                            ##*/
/*##    printableString                                                                         ##*/
/*##                                                                                            ##*/
/*################################################################################################*/
/*################################################################################################*/

static void
printableString ( char *sOutput, int iOutputLen, const char *sInput, int iInputLen )
{
       const char      *printableControlChar[] = {
                       "<NUL>", "<SOH>", "<STX>", "<ETX>",
                       "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
                       "<BS>" , "<HT>" , "<LF>" , "<VT>" ,
                       "<FF>" , "<CR>" , "<SO>" , "<SI>" ,
                       "<DLE>", "<DC1>", "<DC2>", "<DC3>",
                       "<DC4>", "<NAK>", "<SYN>", "<ETB>",
                       "<CAN>", "<EM>" , "<SUB>", "<ESC>",
                       "<FS>" , "<GS>" , "<RS>" , "<US>" ,
                       " " } ;

       size_t  i, j, n ;
       size_t  InputLen;
       size_t  OutputLen;

       InputLen = (size_t)iInputLen;
       OutputLen = (size_t)iOutputLen;
       for ( i = j = 0 ; i < InputLen && j < OutputLen ; i ++ ) {
               if ( isprint( (unsigned char)sInput[i] ) ) {
                       n = 1 ;
                       if ( j + 1 >= OutputLen )
                               break ;
                       sOutput[j] = sInput[i] ;
               } else if ( ( sInput[i] & 0xFF ) <
                           COUNTOF(printableControlChar) ) {
                       n = strlen( printableControlChar[sInput[i] & 0xFF] ) ;
                       if ( j + n + 1 >= OutputLen )
                               break ;
                       strlcpy( sOutput + j,
                                printableControlChar[sInput[i] & 0xFF],
                                OutputLen - j ) ;
               } else {
                       n = 5 ;
                       if ( j + n + 1 >= OutputLen )
                               break ;
                       snprintf( sOutput + j, OutputLen - j, "<x%X>",
                                 sInput[i] & 0xFF ) ;
               }
               j += n ;
       }

       sOutput[min(j, (size_t)iOutputLen - 1)] = '\0' ;

}

/**************************************************************************************************/

#else
NONEMPTY_TRANSLATION_UNIT
#endif /* REFCLOCK */