diff options
author | delphij <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
commit | 2a25cee78ab1d37e7d2bc40ae675646974d99f56 (patch) | |
tree | b0302ac4be59e104f4e1e54014561a1389397192 /contrib/ntp/ntpd/refclock_jjy.c | |
parent | a0741a75537b2e0514472ac3b28afc55a7846c30 (diff) | |
download | FreeBSD-src-2a25cee78ab1d37e7d2bc40ae675646974d99f56.zip FreeBSD-src-2a25cee78ab1d37e7d2bc40ae675646974d99f56.tar.gz |
MFC r280849,280915-280916,281015-281016,282097,282408,282415,283542,
284864,285169-285170,285435:
ntp 4.2.8p3.
Relnotes: yes
Approved by: re (?)
Diffstat (limited to 'contrib/ntp/ntpd/refclock_jjy.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_jjy.c | 4652 |
1 files changed, 3975 insertions, 677 deletions
diff --git a/contrib/ntp/ntpd/refclock_jjy.c b/contrib/ntp/ntpd/refclock_jjy.c index d1707ce..fef829c 100644 --- a/contrib/ntp/ntpd/refclock_jjy.c +++ b/contrib/ntp/ntpd/refclock_jjy.c @@ -3,80 +3,109 @@ */ /**********************************************************************/ -/* */ -/* Copyright (C) 2001-2004, Takao Abe. All rights reserved. */ -/* */ +/* */ +/* Copyright (C) 2001-2015, Takao Abe. All rights reserved. */ +/* */ /* Permission to use, copy, modify, and distribute this software */ -/* and its documentation for any purpose is hereby granted */ +/* 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. */ -/* */ +/* 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. */ -/* */ +/* 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. */ +/* 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 */ +/* ( 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 */ +/* 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. */ +/* voluntary contributions for the NTP. */ /* The manufacturer of the JJY receiver has not participated in */ -/* a development of this driver. */ +/* a development of this driver. */ /* The manufacturer does not warrant anything about this driver, */ -/* and is not liable for anything about this driver. */ -/* */ +/* and is not liable for anything about this driver. */ +/* */ /**********************************************************************/ -/* */ -/* Author Takao Abe */ -/* Email abetakao@bea.hi-ho.ne.jp */ -/* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */ -/* */ +/* */ +/* Author Takao Abe */ +/* Email takao_abe@xurb.jp */ +/* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */ +/* */ +/* The email address abetakao@bea.hi-ho.ne.jp 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 */ +/* takao_abe@xurb.jp */ +/* */ /**********************************************************************/ -/* */ -/* 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 */ +/* */ +/* 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 ( fukusima@goto.info.waseda.ac.jp ) */ -/* */ -/* 2002/07/12 */ -/* [Fix] Portability for FreeBSD ( patched by the user ) */ -/* */ -/* 2004/10/31 */ +/* */ +/* 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 */ -/* */ +/* 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 */ +/* */ /**********************************************************************/ #ifdef HAVE_CONFIG_H @@ -99,132 +128,298 @@ #include "ntp_stdlib.h" /**********************************************************************/ -/* */ -/* The Tristate Ltd. JJY receiver JJY01 */ -/* */ -/* Command Response Remarks */ -/* ------------ ---------------------- --------------------- */ -/* date<CR><LF> YYYY/MM/DD XXX<CR><LF> */ -/* time<CR><LF> HH:MM:SS<CR><LF> */ -/* stim<CR><LF> HH:MM:SS<CR><LF> Reply at just second */ -/* */ -/* During synchronization after a receiver is turned on, */ -/* It replies the past time from 2000/01/01 00:00:00. */ -/* The function "refclock_process" checks the time and tells */ -/* as an insanity time. */ -/* */ -/**********************************************************************/ -/* */ -/* The C-DEX Co. Ltd. JJY receiver JST2000 */ -/* */ -/* Command Response Remarks */ -/* ------------ ---------------------- --------------------- */ -/* <ENQ>1J<ETX> <STX>JYYMMDD HHMMSSS<ETX> */ -/* */ -/**********************************************************************/ -/* */ -/* The Echo Keisokuki Co. Ltd. JJY receiver LT2000 */ -/* */ -/* Command Response Remarks */ -/* ------------ ---------------------- --------------------- */ -/* # Mode 1 (Request&Send) */ -/* T YYMMDDWHHMMSS<BCC1><BCC2><CR> */ -/* C Mode 2 (Continuous) */ -/* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */ -/* <SUB> Second signal */ -/* */ -/**********************************************************************/ -/* */ -/* The CITIZEN T.I.C CO., LTD. JJY receiver JJY200 */ -/* */ -/* Command Response Remarks */ -/* ------------ ---------------------- --------------------- */ -/* 'XX YY/MM/DD W HH:MM:SS<CR> */ -/* XX: OK|NG|ER */ -/* W: 0(Monday)-6(Sunday) */ -/* */ -/**********************************************************************/ /* * Interface definitions */ -#define DEVICE "/dev/jjy%d" /* device name and unit */ -#define SPEED232 B9600 /* uart speed (9600 baud) */ -#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 REFID "JJY" /* reference ID */ +#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) */ +#define PRECISION (-3) /* precision assumed (about 100 ms) */ /* * JJY unit control structure */ + +struct jjyRawDataBreak { + char *pString ; + int iLength ; +} ; + +#define MAX_TIMESTAMP 6 +#define MAX_RAWBUF 100 +#define MAX_LOOPBACK 5 + struct jjyunit { - char unittype ; /* UNITTYPE_XXXXXXXXXX */ - short operationmode ; /* Echo Keisokuki LT-2000 : 1 or 2 */ - short version ; - short linediscipline ; /* LDISC_CLK or LDISC_RAW */ - char bPollFlag ; /* Set by jjy_pool and Reset by jjy_receive */ - int linecount ; - int lineerror ; +/* 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 */ -#define MAX_LINECOUNT 8 -#define MAX_RAWBUF 64 - int lineexpect ; - int charexpect [ MAX_LINECOUNT ] ; - int charcount ; - char rawbuf [ MAX_RAWBUF ] ; + 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 ; +/* 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_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 P((int, struct peer *)); -static void jjy_shutdown P((int, struct peer *)); -static void jjy_poll P((int, struct peer *)); -static void jjy_poll_tristate_jjy01 P((int, struct peer *)); -static void jjy_poll_cdex_jst2000 P((int, struct peer *)); -static void jjy_poll_echokeisokuki_lt2000 P((int, struct peer *)); -static void jjy_poll_citizentic_jjy200 P((int, struct peer *)); -static void jjy_receive P((struct recvbuf *)); -static int jjy_receive_tristate_jjy01 P((struct recvbuf *)); -static int jjy_receive_cdex_jst2000 P((struct recvbuf *)); -static int jjy_receive_echokeisokuki_lt2000 P((struct recvbuf *)); -static int jjy_receive_citizentic_jjy200 P((struct recvbuf *)); + +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 */ - NOFLAGS /* not used */ + 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 +#define RC_START_ERROR 0 /* * Local constants definition */ -#define MAX_LOGTEXT 64 +#define MAX_LOGTEXT 100 +#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 + +/* 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_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 */ @@ -233,145 +428,123 @@ static int jjy_start ( int unit, struct peer *peer ) { - struct jjyunit *up ; - struct refclockproc *pp ; + struct refclockproc *pp ; + struct jjyunit *up ; + int rc ; int fd ; - char *pDeviceName ; - short iDiscipline ; - int iSpeed232 ; + char sDeviceName [ sizeof(DEVICE) + 10 ], sLog [ 60 ] ; #ifdef DEBUG if ( debug ) { - printf ( "jjy_start (refclock_jjy.c) : %s mode=%d ", ntoa(&peer->srcadr), peer->ttl ) ; - printf ( DEVICE, unit ) ; - printf ( "\n" ) ; + printf( "refclock_jjy.c : jjy_start : %s mode=%d dev=%s unit=%d\n", + ntoa(&peer->srcadr), peer->ttl, DEVICE, unit ) ; } #endif - /* - * Open serial port - */ - if ( ! ( pDeviceName = (char*) emalloc ( strlen(DEVICE) + 10 ) ) ) { - return RC_START_ERROR ; - } - sprintf ( pDeviceName, DEVICE, unit ) ; - /* - * 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 : - iDiscipline = LDISC_CLK ; - iSpeed232 = SPEED232_TRISTATE_JJY01 ; - break ; - case 2 : - iDiscipline = LDISC_RAW ; - iSpeed232 = SPEED232_CDEX_JST2000 ; - break ; - case 3 : - iDiscipline = LDISC_CLK ; - iSpeed232 = SPEED232_ECHOKEISOKUKI_LT2000 ; - break ; - case 4 : - iDiscipline = LDISC_CLK ; - iSpeed232 = SPEED232_CITIZENTIC_JJY200 ; - break ; - default : - msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode", - ntoa(&peer->srcadr), peer->ttl ) ; - free ( (void*) pDeviceName ) ; + /* 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) ) ; - if ( ! ( fd = refclock_open ( pDeviceName, iSpeed232, iDiscipline ) ) ) { - free ( (void*) pDeviceName ) ; - return RC_START_ERROR ; - } - free ( (void*) pDeviceName ) ; + 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 ; - /* - * Allocate and initialize unit structure - */ - if ( ! ( up = (struct jjyunit *) emalloc (sizeof(struct jjyunit)) ) ) { - close ( fd ) ; - return RC_START_ERROR ; - } + /* Set up the device name */ + snprintf( sDeviceName, sizeof(sDeviceName), DEVICE, unit ) ; - memset ( (char*)up, 0, sizeof(struct jjyunit) ) ; - up->linediscipline = iDiscipline ; + 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 : - /* - * The mode 0 is a default clock type at this time. - * But this will be change to auto-detect mode in the future. - */ case 1 : - up->unittype = UNITTYPE_TRISTATE_JJY01 ; - up->version = 100 ; - up->lineexpect = 2 ; - up->charexpect[0] = 14 ; /* YYYY/MM/DD WWW<CR><LF> */ - up->charexpect[1] = 8 ; /* HH:MM:SS<CR><LF> */ + rc = jjy_start_tristate_jjy01 ( unit, peer, up ) ; break ; case 2 : - up->unittype = UNITTYPE_CDEX_JST2000 ; - up->lineexpect = 1 ; - up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */ + rc = jjy_start_cdex_jst2000 ( unit, peer, up ) ; break ; case 3 : - up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ; - up->operationmode = 2 ; /* Mode 2 : Continuous mode */ - up->lineexpect = 1 ; - switch ( up->operationmode ) { - case 1 : - up->charexpect[0] = 15 ; /* YYMMDDWHHMMSS<BCC1><BCC2><CR> */ - break ; - case 2 : - up->charexpect[0] = 17 ; /* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */ - break ; - } + 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 ; - case 4 : - up->unittype = UNITTYPE_CITIZENTIC_JJY200 ; - up->lineexpect = 1 ; - up->charexpect[0] = 23 ; /* 'XX YY/MM/DD W HH:MM:SS<CR> */ - break ; default : - msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode", - ntoa(&peer->srcadr), peer->ttl ) ; - close ( fd ) ; + 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 ( sDeviceName, up->linespeed, up->linediscipline ) ; + if ( fd <= 0 ) { free ( (void*) up ) ; return RC_START_ERROR ; } + /* + * Initialize variables + */ pp = peer->procptr ; - pp->unitptr = (caddr_t) up ; + + pp->clockdesc = DESCRIPTION ; + pp->unitptr = up ; pp->io.clock_recv = jjy_receive ; - pp->io.srcclock = (caddr_t) peer ; - pp->io.datalen = 0 ; - pp->io.fd = fd ; + pp->io.srcclock = peer ; + pp->io.datalen = 0 ; + pp->io.fd = fd ; if ( ! io_addclock(&pp->io) ) { close ( fd ) ; - free ( (void*) up ) ; + pp->io.fd = -1 ; + free ( up ) ; + pp->unitptr = NULL ; return RC_START_ERROR ; } + memcpy( (char*)&pp->refid, REFID, strlen(REFID) ) ; - /* - * Initialize miscellaneous variables - */ peer->precision = PRECISION ; - peer->burst = 1 ; - pp->clockdesc = DESCRIPTION ; - memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ; + + 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 */ /**************************************************************************************************/ @@ -379,16 +552,24 @@ static void jjy_shutdown ( int unit, struct peer *peer ) { - struct jjyunit *up; + struct jjyunit *up; struct refclockproc *pp; + char sLog [ 60 ] ; + pp = peer->procptr ; - up = (struct jjyunit *) pp->unitptr ; - io_closeclock ( &pp->io ) ; - free ( (void*) up ) ; + 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 */ @@ -396,129 +577,438 @@ jjy_shutdown ( int unit, struct peer *peer ) static void jjy_receive ( struct recvbuf *rbufp ) { +#ifdef DEBUG + static const char *sFunctionName = "jjy_receive" ; +#endif - struct jjyunit *up ; + struct jjyunit *up ; struct refclockproc *pp ; - struct peer *peer; + struct peer *peer; l_fp tRecvTimestamp; /* arrival timestamp */ int rc ; - char sLogText [ MAX_LOGTEXT ] ; - int i, bCntrlChar ; + char *pBuf, sLogText [ MAX_LOGTEXT ] ; + int iLen, iCopyLen ; + int i, j, iReadRawBuf, iBreakPosition ; /* * Initialize pointers and read the timecode and timestamp */ - peer = (struct peer *) rbufp->recv_srcclock ; + peer = rbufp->recv_peer ; pp = peer->procptr ; - up = (struct jjyunit *) pp->unitptr ; + up = pp->unitptr ; /* * Get next input line */ - pp->lencode = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ; - 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. */ + /* - * The reply with <STX> and <ETX> may give a blank line - */ - if ( pp->lencode == 0 && up->charcount == 0 ) return ; - /* - * Copy received charaters to temporary buffer + * Append received charaters to temporary buffer */ - for ( i = 0 ; i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ; i ++ , up->charcount ++ ) { - up->rawbuf[up->charcount] = pp->a_lastcode[i] ; - } - while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) { - for ( i = 0 ; i < up->charcount - 1 ; i ++ ) up->rawbuf[i] = up->rawbuf[i+1] ; - up->charcount -- ; + for ( i = 0 ; + i < pp->lencode && up->iRawBufLen < MAX_RAWBUF - 2 ; + i ++ , up->iRawBufLen ++ ) { + up->sRawBuf[up->iRawBufLen] = pp->a_lastcode[i] ; } - bCntrlChar = 0 ; - for ( i = 0 ; i < up->charcount ; i ++ ) { - if ( up->rawbuf[i] < ' ' ) { - bCntrlChar = 1 ; - break ; - } + 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( pp->a_lastcode[i] & 0x7F ) ) { + printf( "<x%02X>", pp->a_lastcode[i] & 0xFF ) ; + } else { + printf( "%c", pp->a_lastcode[i] ) ; } - if ( pp->lencode > 0 && up->linecount < up->lineexpect ) { - if ( bCntrlChar == 0 && up->charcount < up->charexpect[up->linecount] ) return ; + } + 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 ) ; } - up->rawbuf[up->charcount] = 0 ; - } else { - /* - * The reply with <CR><LF> gives a blank line - */ - if ( pp->lencode == 0 ) return ; +#endif + return ; } + /* * We get down to business */ pp->lastrec = tRecvTimestamp ; - up->linecount ++ ; + 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 charaters 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( 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 ) ; + strncpy( 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 ; + + } + } + } + } - if ( up->lineerror != 0 ) return ; +#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 : - rc = jjy_receive_tristate_jjy01 ( rbufp ) ; + jjy_poll_tristate_jjy01 ( unit, peer ) ; break ; case UNITTYPE_CDEX_JST2000 : - rc = jjy_receive_cdex_jst2000 ( rbufp ) ; + jjy_poll_cdex_jst2000 ( unit, peer ) ; break ; case UNITTYPE_ECHOKEISOKUKI_LT2000 : - rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ; + 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_CITIZENTIC_JJY200 : - rc = jjy_receive_citizentic_jjy200 ( rbufp ) ; - break ; + case UNITTYPE_TELEPHONE : + jjy_poll_telephone ( unit, peer ) ; + break ; default : - rc = 0 ; break ; } - if ( up->linediscipline == LDISC_RAW ) { - if ( up->linecount <= up->lineexpect && up->charcount > up->charexpect[up->linecount-1] ) { - for ( i = 0 ; i < up->charcount - up->charexpect[up->linecount-1] ; i ++ ) { - up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ; - } - up->charcount -= up->charexpect[up->linecount-1] ; - } else { - up->charcount = 0 ; - } - } +} - if ( rc == 0 ) return ; +/**************************************************************************************************/ +/* jjy_timer - called at one-second intervals */ +/**************************************************************************************************/ +static void +jjy_timer ( int unit, struct peer *peer ) +{ - up->bPollFlag = 0 ; + struct refclockproc *pp ; + struct jjyunit *up ; - if ( up->lineerror != 0 ) { - refclock_report ( peer, CEVNT_BADREPLY ) ; - strcpy ( sLogText, "BAD REPLY [" ) ; - if ( up->linediscipline == LDISC_RAW ) { - strncat ( sLogText, up->rawbuf, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ; - } else { - strncat ( sLogText, pp->a_lastcode, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ; +#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" ) ; } - sLogText[MAX_LOGTEXT-1] = 0 ; - if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 ) strcat ( sLogText, "]" ) ; - record_clock_stats ( &peer->srcadr, sLogText ) ; +#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 [ 80 ], cStatus ; + const char *pStatus ; + pp->year = up->year ; - pp->day = ymd2yd ( up->year, up->month, up->day ) ; + 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; + pp->nsec = up->msecond * 1000000 ; /* * JST to UTC @@ -529,631 +1019,3439 @@ jjy_receive ( struct recvbuf *rbufp ) pp->day -- ; if ( pp->day < 1 ) { pp->year -- ; - pp->day = ymd2yd ( pp->year, 12, 31 ) ; + 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 ; + + return 0 ; + +} + +/**************************************************************************************************/ + +static int +jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp ) +{ + struct jjyunit *up ; + struct refclockproc *pp ; + struct peer *peer; + + char *pBuf, sLog [ 100 ] ; + 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_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 + || strncmp( pBuf, TS_JJY01_REPLY_STUS_UNADJUSTED, + TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED ) == 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 ; + + 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 ) { + /* 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 excuted 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 ; + } + #ifdef DEBUG if ( debug ) { - printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d.%1d JST ", - up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond/100 ) ; - printf ( "( %04d/%03d %02d:%02d:%02d.%1d UTC )\n", - pp->year, pp->day, pp->hour, pp->minute, pp->second, (int)(pp->nsec/100000000) ) ; + printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->iLineCount=%d\n", + sFunctionName, pp->sloppyclockflag, CLK_FLAG1, + up->iLineCount ) ; } #endif /* - * Process the new sample in the median filter and determine the - * timecode timestamp. + * Send a first command */ - sprintf ( sLogText, "%04d/%02d/%02d %02d:%02d:%02d.%1d JST", - up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond/100 ) ; - record_clock_stats ( &peer->srcadr, sLogText ) ; + up->iCommandSeq ++ ; - if ( ! refclock_process ( pp ) ) { - refclock_report(peer, CEVNT_BADTIME); - return ; + 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 ) ; } - pp->lastref = pp->lastrec; - refclock_receive(peer); + 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>JYYMMDD HHMMSSS<ETX> J is a fixed character */ +/* */ +/*################################################################################################*/ + +static struct jjyRawDataBreak cdex_jst2000_raw_break [ ] = +{ + { "\x03", 1 }, { NULL, 0 } +} ; + /**************************************************************************************************/ static int -jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp ) +jjy_start_cdex_jst2000 ( int unit, struct peer *peer, struct jjyunit *up ) { - static char *sFunctionName = "jjy_receive_tristate_jjy01" ; + 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; + struct peer *peer ; - char *pBuf ; + char *pBuf, sLog [ 100 ] ; 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 ; + } + + /* JYYMMDD HHMMSSS */ + + rc = sscanf ( pBuf, "J%2d%2d%2d %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 ; + /* - * Initialize pointers and read the timecode and timestamp + * Send "<ENQ>1J<ETX>" command */ - peer = (struct peer *) rbufp->recv_srcclock ; + + 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 = (struct jjyunit *) pp->unitptr ; + up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { - pBuf = up->rawbuf ; - iLen = up->charcount ; + pBuf = up->sTextBuf ; + iLen = up->iTextBufLen ; } else { - pBuf = pp->a_lastcode ; - iLen = pp->lencode ; + pBuf = pp->a_lastcode ; + iLen = pp->lencode ; } - switch ( up->linecount ) { + 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 ; + } - case 1 : /* YYYY/MM/DD WWW */ + if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen == 15 ) { + /* YYMMDDWHHMMSS<BCC1><BCC2> */ - if ( iLen != 14 ) { -#ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Reply length error ( up->linecount=%d iLen=%d )\n", sFunctionName, up->linecount, iLen ) ; - } -#endif - up->lineerror = 1 ; - break ; + for ( i = ibcc = 0 ; i < 13 ; i ++ ) { + ibcc ^= pBuf[i] ; } - rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ; - if ( rc != 3 || up->year < 2000 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 ) { -#ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Date error ( up->linecount=%d )\n", sFunctionName, up->linecount ) ; - } -#endif - up->lineerror = 1 ; - break ; + + 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 ; } - /*** Start of modification on 2004/10/31 */ - /* - * Following codes are moved from the function jjy_poll_tristate_jjy01 in this source. - * The Tristate JJY-01 ( Firmware version 1.01 ) accepts "time" and "stim" commands without any delay. - * But the JJY-01 ( Firmware version 2.01 ) does not accept these commands continuously, - * so this driver issues the second command "stim" after the reply of the first command "date". - */ + } - /* - * Send "stim<CR><LF>" or "time<CR><LF>" command - */ - + 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> */ - if ( up->version >= 100 ) { -#ifdef DEBUG - if ( debug ) { - printf ( "%s (refclock_jjy.c) : send 'stim<CR><LF>'\n", sFunctionName ) ; - } -#endif - if ( write ( pp->io.fd, "stim\r\n",6 ) != 6 ) { - refclock_report ( peer, CEVNT_FAULT ) ; - } - } else { -#ifdef DEBUG - if ( debug ) { - printf ( "%s (refclock_jjy.c) : send 'time<CR><LF>'\n", sFunctionName ) ; - } -#endif - if ( write ( pp->io.fd, "time\r\n",6 ) != 6 ) { - refclock_report ( peer, CEVNT_FAULT ) ; + 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 -- ; + } + } + } + } } + } - /*** End of modification ***/ - return 0 ; + 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. */ - case 2 : /* HH:MM:SS */ + 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 ; + +} - if ( iLen != 8 ) { +/**************************************************************************************************/ + +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/respose mode. */ +/* When this jjy driver are used, set to command/respose 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 - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Reply length error ( up->linecount=%d iLen=%d )\n", sFunctionName, up->linecount, iLen ) ; - } + static const char *sFunctionName = "jjy_receive_tristate_gpsclock01" ; #endif - up->lineerror = 1 ; - break ; + + struct jjyunit *up ; + struct refclockproc *pp ; + struct peer *peer; + + char *pBuf, sLog [ 100 ] ; + 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 ) ; } - rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ; - if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { +#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 >= 2 ) { - printf ( "%s (refclock_jjy.c) : Time error ( up->linecount=%d )\n", sFunctionName, up->linecount ) ; - } + if ( debug ) { + printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n", + sFunctionName, pBuf ) ; + } #endif - up->lineerror = 1 ; - break ; + 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 ; - if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) { - /* - * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver continuously. - * But the JJY receiver replies a date and time separately. - * Just after midnight transitions, we ignore this time. - */ - return 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 */ - up->lineerror = 1 ; - break ; + 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 excuted 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 ) ; } - return 1 ; + jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; + + return JJY_RECEIVE_WAIT ; } /**************************************************************************************************/ -static int -jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp ) +static void +jjy_poll_tristate_gpsclock01 ( int unit, struct peer *peer ) { +#ifdef DEBUG + static const char *sFunctionName = "jjy_poll_tristate_gpsclock01" ; +#endif - static char *sFunctionName = "jjy_receive_cdex_jst2000" ; - - struct jjyunit *up ; struct refclockproc *pp ; - struct peer *peer; + struct jjyunit *up ; - char *pBuf ; - int iLen ; - int rc ; + 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 /* - * Initialize pointers and read the timecode and timestamp + * Send a first command */ - peer = (struct peer *) rbufp->recv_srcclock ; + + 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 [ 100 ] ; + int iLen, i ; + int rc, iWeekday ; + time_t now ; + struct tm *pTime ; + + /* Initialize pointers */ + + peer = rbufp->recv_peer ; pp = peer->procptr ; - up = (struct jjyunit *) pp->unitptr ; + up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { - pBuf = up->rawbuf ; - iLen = up->charcount ; + pBuf = up->sTextBuf ; + iLen = up->iTextBufLen ; } else { - pBuf = pp->a_lastcode ; - iLen = pp->lencode ; + 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 ; } - switch ( up->linecount ) { + /* Process timestamp */ - case 1 : /* JYYMMDD HHMMSSS */ + up->iReceiveSeq ++ ; - if ( iLen != 15 ) { -#ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n", sFunctionName, iLen ) ; - } -#endif - up->lineerror = 1 ; - break ; + switch ( iLen ) { + + case 8 : /* Type 1 : <STX>HH:MM:SS<ETX> */ + + for ( i = 0 ; i < iLen ; i ++ ) { + pBuf[i] &= 0x7F ; } - 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 + + 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 ) { -#ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d.%1d ]\n", sFunctionName, - rc, up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond ) ; - } -#endif - up->lineerror = 1 ; - break ; + /* 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 ; } - up->year += 2000 ; - up->msecond *= 100 ; + + time( &now ) ; + pTime = localtime( &now ) ; + up->year = pTime->tm_year ; + up->month = pTime->tm_mon + 1 ; + up->day = pTime->tm_mday ; + break ; - default : /* Unexpected reply */ + 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 ; + } - up->lineerror = 1 ; 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 ; + } - return 1 ; + 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 ] ) ( ) = +{ /*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_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp ) +jjy_start_telephone ( int unit, struct peer *peer, struct jjyunit *up ) { - static char *sFunctionName = "jjy_receive_echokeisokuki_lt2000" ; + char sLog [ 80 ], sFirstThreeDigits [ 4 ] ; + int i, iNumberOfDigitsOfPhoneNumber, iCommaCount, iCommaPosition ; + int iFirstThreeDigitsCount ; - struct jjyunit *up ; - struct refclockproc *pp ; - struct peer *peer; + 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( *(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 ; - int rc ; - int i, ibcc, ibcc1, ibcc2 ; + int iLen ; + short iPreviousModemState ; - /* - * Initialize pointers and read the timecode and timestamp - */ - peer = (struct peer *) rbufp->recv_srcclock ; + peer = rbufp->recv_peer ; pp = peer->procptr ; - up = (struct jjyunit *) pp->unitptr ; + 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->rawbuf ; - iLen = up->charcount ; + pBuf = up->sTextBuf ; + iLen = up->iTextBufLen ; } else { - pBuf = pp->a_lastcode ; - iLen = pp->lencode ; + pBuf = pp->a_lastcode ; + iLen = pp->lencode ; } - switch ( up->linecount ) { + 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 ) ; - case 1 : /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */ + return JJY_RECEIVE_WAIT ; - if ( ( up->operationmode == 1 && iLen != 15 ) || ( up->operationmode == 2 && iLen != 17 ) ) { +} + +/**************************************************************************************************/ + +static void +jjy_poll_telephone ( int unit, struct peer *peer ) +{ #ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n", sFunctionName, iLen ) ; - } + static const char *sFunctionName = "jjy_poll_telephone" ; #endif - if ( up->operationmode == 1 ) { + + 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 - if ( debug ) { - printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ; - } + static const char *sFunctionName = "jjy_timer_telephone" ; #endif - if ( write ( pp->io.fd, "#",1 ) != 1 ) { - refclock_report ( peer, CEVNT_FAULT ) ; - } + + 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 ) ; } - up->lineerror = 1 ; - break ; } - if ( up->operationmode == 1 ) { + } + +} + +/**************************************************************************************************/ + +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 ) ; - 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 ) { + if ( rc == TELJJY_CHANGE_CLOCK_STATE ) { + iPostEvent = iTeljjyPostEvent[up->iClockEvent][up->iClockState] ; #ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : BCC error ( Recv=%02X,%02X / Calc=%02X,%02X)\n", sFunctionName, pBuf[13]&0xFF, pBuf[14]&0xFF, ibcc1, ibcc2 ) ; - } + if ( debug ) { + printf( "refclock_jjy.c : teljjy_control : iClockState=%hd -> %hd iPostEvent=%hd\n", + up->iClockState, iTeljjyNextState[up->iClockEvent][up->iClockState], iPostEvent ) ; + } #endif - up->lineerror = 1 ; - break ; + 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 ) ; + } - 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 ) { + 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 ) +{ + + 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 ; + + 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 >= 2 ) { - printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d ]\n", sFunctionName, - rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; - } + if ( debug ) { + printf( "refclock_jjy.c : teljjy_conn_send : iLoopbackCount=%d\n", + up->iLoopbackCount ) ; + } #endif - up->lineerror = 1 ; - break ; + + 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 ) ; } - up->year += 2000 ; + if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) { + /* Loopback character and timestamp */ + gettimeofday( &(up->sendTime[up->iLoopbackCount]), NULL ) ; + up->bLoopbackMode = TRUE ; + } else { + /* Regular command */ + up->bLoopbackMode = FALSE ; + } - if ( up->operationmode == 2 ) { + jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; - /* A time stamp comes on every 0.5 seccond in the mode 2 of the LT-2000. */ - up->msecond = 500 ; - pp->second -- ; - if ( pp->second < 0 ) { - pp->second = 59 ; - pp->minute -- ; - if ( pp->minute < 0 ) { - pp->minute = 59 ; - pp->hour -- ; - if ( pp->hour < 0 ) { - pp->hour = 23 ; - pp->day -- ; - if ( pp->day < 1 ) { - pp->year -- ; - pp->day = ymd2yd ( pp->year, 12, 31 ) ; - } - } - } - } + 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 ; + } - /* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */ -#ifdef DEBUG - if ( debug ) { - printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ; - } + } 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 [ 80 ] ; + 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 - if ( write ( pp->io.fd, "#",1 ) != 1 ) { - refclock_report ( peer, CEVNT_FAULT ) ; + 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 ) ; + } + } + + } } - break ; + } else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength != iLen + && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) { + /* Loopback noise ( Unexpected replay ) */ - default : /* Unexpected reply */ + 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 ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ; + printf( "refclock_jjy.c : teljjy_conn_silent : call teljjy_conn_send\n" ) ; } #endif - if ( write ( pp->io.fd, "#",1 ) != 1 ) { - refclock_report ( peer, CEVNT_FAULT ) ; + 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" ; + } - up->lineerror = 1 ; - break ; - + if ( write( pp->io.fd, pCmd, 1 ) != 1 ) { + refclock_report( peer, CEVNT_FAULT ) ; } - return 1 ; + 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 ] ) ( ) = +{ /*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 -jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp ) +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; - static char *sFunctionName = "jjy_receive_citizentic_jjy200" ; + pp = peer->procptr ; + up = pp->unitptr ; - struct jjyunit *up ; - struct refclockproc *pp ; - struct peer *peer; + DEBUG_MODEM_PRINTF( "modem_disconnect" ) ; - char *pBuf ; - int iLen ; - int rc ; - char cApostrophe, sStatus[3] ; - int iWeekday ; + up->iModemEvent = MODEM_EVENT_DISCONNECT ; - /* - * Initialize pointers and read the timecode and timestamp - */ - peer = (struct peer *) rbufp->recv_srcclock ; - pp = peer->procptr ; - up = (struct jjyunit *) pp->unitptr ; + modem_control ( peer, pp, up ) ; - if ( up->linediscipline == LDISC_RAW ) { - pBuf = up->rawbuf ; - iLen = up->charcount ; - } else { - pBuf = pp->a_lastcode ; - iLen = pp->lencode ; - } +} - /* - * JJY-200 sends a timestamp every second. - * So, a timestamp is ignored unless it is right after polled. - */ - if ( ! up->bPollFlag ) return 0 ; +/**************************************************************************************************/ - switch ( up->linecount ) { +static int +modem_receive ( struct recvbuf *rbufp ) +{ - case 1 : /* 'XX YY/MM/DD W HH:MM:SS<CR> */ + struct peer *peer; + struct jjyunit *up; + struct refclockproc *pp; + char *pBuf ; + int iLen ; - if ( iLen != 23 ) { #ifdef DEBUG - if ( debug >= 2 ) { - printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n", sFunctionName, iLen ) ; - } + static const char *sFunctionName = "modem_receive" ; #endif - up->lineerror = 1 ; - break ; - } - - 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 - || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 - || iWeekday > 6 - || up->hour > 23 || up->minute > 59 || up->second > 60 ) { + + 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 >= 2 ) { - printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %c %2s %02d %02d %02d %d %02d %02d %02d ]\n", sFunctionName, - rc, cApostrophe, sStatus, up->year, up->month, up->day, iWeekday, up->hour, up->minute, up->second ) ; - } + if ( debug ) { + char sResp [ 40 ] ; + int 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=%d pBuf=[%s] iModemEvent=%d\n", iCopyLen, sResp, up->iModemEvent ) ; + } #endif - up->lineerror = 1 ; - break ; - } + modem_control ( peer, pp, up ) ; - up->year += 2000 ; - up->msecond = 0 ; + return 0 ; - break ; +} - default : /* Unexpected reply */ +/**************************************************************************************************/ - up->lineerror = 1 ; - break ; +static void +modem_timer ( int unit, struct peer *peer ) +{ - } + struct refclockproc *pp ; + struct jjyunit *up ; - return 1 ; + 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 ) ; + } + } } /**************************************************************************************************/ -/* jjy_poll - called by the transmit procedure */ -/**************************************************************************************************/ + static void -jjy_poll ( int unit, struct peer *peer ) +modem_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { - struct jjyunit *up; - struct refclockproc *pp; + int rc ; + short iPostEvent = MODEM_EVENT_NULL ; - pp = peer->procptr; - up = (struct jjyunit *) pp->unitptr ; + DEBUG_MODEM_PRINTF( "modem_control" ) ; - if ( pp->polls > 0 && up->linecount == 0 ) { - /* - * No reply for last command - */ - refclock_report ( peer, CEVNT_TIMEOUT ) ; + 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 ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ; + printf( "refclock_jjy.c : modem_init_start : call modem_init_resp00\n" ) ; } #endif - pp->polls ++ ; + return modem_init_resp00( peer, pp, up ) ; + +} - up->bPollFlag = 1 ; - up->linecount = 0 ; - up->lineerror = 0 ; - up->charcount = 0 ; +/******************************/ +static int +modem_init_resp00 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) +{ - switch ( up->unittype ) { - - case UNITTYPE_TRISTATE_JJY01 : - jjy_poll_tristate_jjy01 ( unit, peer ) ; + char *pCmd, 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 UNITTYPE_CDEX_JST2000 : - jjy_poll_cdex_jst2000 ( unit, peer ) ; + 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( pCmd, sizeof(cBuf), "ATM%dL%d\r\n", iSpeakerSwitch, iSpeakerVolume ) ; break ; - case UNITTYPE_ECHOKEISOKUKI_LT2000 : - jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ; + case 3 : + /* &Kn = Flow control 4:XON/XOFF */ + pCmd = "AT&K4\r\n" ; break ; - case UNITTYPE_CITIZENTIC_JJY200 : - jjy_poll_citizentic_jjy200 ( unit, peer ) ; - break ; + case 4 : + /* +MS = Protocol V22B:1200,2400bpsiV.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( pCmd, 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 ) ; + } -static void -jjy_poll_tristate_jjy01 ( int unit, struct peer *peer ) + 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 ) { - struct refclockproc *pp; + DEBUG_MODEM_PRINTF( "modem_init_resp04" ) ; - pp = peer->procptr; + return modem_init_resp00( peer, pp, up ) ; - /* - * Send "date<CR><LF>" command - */ +} + +/******************************/ +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 ( "jjy_poll_tristate_jjy01 (refclock_jjy.c) : send 'date<CR><LF>'\n" ) ; + printf( "refclock_jjy.c : modem_init_disc : call modem_esc_disc\n" ) ; } #endif - if ( write ( pp->io.fd, "date\r\n",6 ) != 6 ) { - refclock_report ( peer, CEVNT_FAULT ) ; - } + return CHANGE_MODEM_STATE ; } -/**************************************************************************************************/ +/******************************/ +static int +modem_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) +{ -static void -jjy_poll_cdex_jst2000 ( int unit, struct peer *peer ) + DEBUG_MODEM_PRINTF( "modem_dial_ignore" ) ; + + return STAY_MODEM_STATE ; + +} + +/******************************/ +static int +modem_dial_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { - struct refclockproc *pp; + char sCmd [ 46 ] ; + int iCmdLen ; + char cToneOrPulse ; - pp = peer->procptr; + DEBUG_MODEM_PRINTF( "modem_dial_dialout" ) ; - /* - * Send "<ENQ>1J<ETX>" command - */ + /* 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 ( "jjy_poll_cdex_jst2000 (refclock_jjy.c) : send '<ENQ>1J<ETX>'\n" ) ; + printf( "refclock_jjy.c : modem_dial_escape : call modem_conn_escape\n" ) ; } #endif - if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4 ) { - refclock_report ( peer, CEVNT_FAULT ) ; + 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 ) +{ -static void -jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer ) + DEBUG_MODEM_PRINTF( "modem_conn_ignore" ) ; + + return STAY_MODEM_STATE ; + +} + +/******************************/ +static int +modem_conn_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { - struct jjyunit *up; - struct refclockproc *pp; + DEBUG_MODEM_PRINTF( "modem_conn_escape" ) ; - char sCmd[2] ; + return CHANGE_MODEM_STATE ; - pp = peer->procptr; - up = (struct jjyunit *) pp->unitptr ; +} - /* - * Send "T" or "C" command - */ +/******************************/ +static int +modem_esc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) +{ - switch ( up->operationmode ) { - case 1 : sCmd[0] = 'T' ; break ; - case 2 : sCmd[0] = 'C' ; break ; + DEBUG_MODEM_PRINTF( "modem_esc_ignore" ) ; + + return STAY_MODEM_STATE ; + +} + +/******************************/ +static int +modem_esc_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) +{ + + 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 ; } - sCmd[1] = 0 ; #ifdef DEBUG if ( debug ) { - printf ( "jjy_poll_echokeisokuki_lt2000 (refclock_jjy.c) : send '%s'\n", sCmd ) ; + printf( "refclock_jjy.c : modem_esc_silent : call modem_esc_disc\n" ) ; } #endif + return modem_esc_disc( peer, pp, up ) ; - if ( write ( pp->io.fd, sCmd, 1 ) != 1 ) { - refclock_report ( peer, CEVNT_FAULT ) ; +} +/******************************/ +static int +modem_esc_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) +{ + + 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_poll_citizentic_jjy200 ( int unit, struct peer *peer ) +jjy_write_clockstats ( struct peer *peer, int iMark, const char *pData ) +{ + + char sLog [ 100 ] ; + 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 ; + 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 ; + } - /* Do nothing ( up->bPollFlag is set by the jjy_poll ) */ + sOutput[min(j, (size_t)iOutputLen - 1)] = '\0' ; } +/**************************************************************************************************/ + #else int refclock_jjy_bs ; #endif /* REFCLOCK */ |