#ident "$Id: x25clock.c,v 1.6 1996/11/18 22:30:23 gary Exp $"
/*****************************************************************************
 * Copyright (c) 1988-95 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/*******************************************************************
 *
 *  SpiderX25 X25 PLP Driver
 *
 *  Copyright 1991 Spider Systems Limited
 *
 *  X25CLOCK.C
 *
 *
 *******************************************************************/

/*
 *	x25clock.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25clock.c
 *	@(#)x25clock.c	1.12
 *
 *	Last delta created	14:28:34 11/6/91
 *	This file extracted	18:32:52 11/19/92
 *
 */

/* ----------------------------------------------------------------------
   X25 code for timers - both the PLP and circuit timers are included
   in this file.  Subroutines are declared here while macros are
   declared in the file X25clock.h

   Circuit Timers:

      T21 - Call request      *   T22 - Reset request *
      T23 - Clear request     *   T24 - Window status (local)
      T25 - Window rotation (remote)
      T26 - Interrupt request *

   Those asterisked, when running, are mutually incompatible (state)
   and occupy the same position within the xtock[] array:

      xtock[0]  :  T21, 22, 23, 26
      xtock[1]  :  T24  (the acknowledgement timer)
      xtock[2]  :  ackdelay (rr suppression timer)
      xtock[3]  :  T25

   LSAP timers:

       T20 - Restart request   - ltock[1]
       Connect timer and
       idle timer (no circuits) - ltock[2]
       random timer (DCE/DTE)   - ltock[3]

   Note that the connect and idle timer occupy the same position in
   the array - they are mutually incompatible with the state.

   ----------------------------------------------------------------------
*/

/* ---- Files 'included' ---- */

#include "x25_inc.h"

#include "x25cause.h"
#include "x25clock.h"
#include "x25form.mac"
#include "iso.h"
#include "x25pti.h"

#ifndef NULL
#define NULL 0
#endif

/*
 * Required Functional Prototypes
 */
static void xtock0expired(struct x25vcformat FAR *);
static void ack_expired(struct x25vcformat FAR *);
static void T20expired(struct lsformat FAR *);
static void T24expired(struct x25vcformat FAR *);
static void T25expired(struct x25vcformat FAR *);
static void ltock2expired(struct lsformat FAR *);
static void random_expired(struct lsformat FAR *);

/* ----------------------------------------------------------------------
            start clock routines
   ---------------------------------------------------------------------- */

/************************************************************************/
void startT22(vv)
/************************************************************************/

/*
    Start the Reset Request timer, stopping all others
*/

register struct x25vcformat FAR *vv;

{
    vv->xtock[0]=(ushort)vv->xbase->T22value;
    vv->xtock[1]=vv->xtock[2]=vv->xtock[3]=0;
}

/************************************************************************/
void startT23(vv)
/************************************************************************/

/*
    Start the Clear Request timer, stopping all others
*/

register struct x25vcformat FAR *vv;

{
    vv->xtock[0]=(ushort)vv->xbase->T23value;
    vv->xtock[1]=vv->xtock[2]=vv->xtock[3]=0;
}

static int lst_rnd_value = 0;

/************************************************************************/
void start_random_timer(ll)
/************************************************************************/

/*
    Start a random timer. Used for generating restart requests
*/

register struct lsformat FAR *ll;

{
    extern int x25ticks;

    /*
	Obtain a value based on last value and 'x25ticks' clock
    */
    if( ll->lbase->L3PLPMODE == 2 )
	ll->ltock[3] = lst_rnd_value = (((lst_rnd_value + x25ticks)&15) + 1);
    else
	ll->ltock[3]=2; /* Take second tick for the WAN case */
}

/************************************************************************/
void stop_vc_timers(vv)
/************************************************************************/

/*
    Stop all timers for a given virtual circuit
*/

register struct x25vcformat FAR *vv;

{
    vv->xtock[0]=vv->xtock[1]=vv->xtock[2]=vv->xtock[3]=0;
}


/************************************************************************/
void stop_ls_timers(ll)
/************************************************************************/

/*
    Stop all timers for a given PLP record
*/

register struct lsformat FAR *ll;

{
    ll->ltock[1]=ll->ltock[2]=ll->ltock[3]=0;
}

/* ----------------------------------------------------------------------
   Routines to examine the state of a clock.  Called during the schedule
   loop for circuits and the PLP databases.
   ---------------------------------------------------------------------- */

/************************************************************************/
void x_examtimer(xvc)
/************************************************************************/

/*
    Examine status of clocks on the given virtual circuit.
*/

register struct x25vcformat FAR *xvc;

{
    if( xvc->xtock[0] != 0 && --(xvc->xtock[0])==0 )
    {
	xtock0expired(xvc);	/* T21,22,23,or 26 expired */
	return;
    }

    if( xvc->xtock[1] != 0 && --(xvc->xtock[1])==0 )
    {
	T24expired(xvc); 	/* Window status expired */
	return;
    }

    if( xvc->xtock[2] != 0 && --(xvc->xtock[2])==0 )
    {
	ack_expired(xvc);        /* rr suppression expired */
	return;
    }

    if( xvc->xtock[3] != 0 && --(xvc->xtock[3])==0 )
    {
	T25expired(xvc);	/* Window rotaton expired */
	return;
    }
}

/************************************************************************/
void l_examtimer(lsp)
/************************************************************************/

/*
    Examine status of clocks on the given PLP record
*/

register struct lsformat FAR *lsp;

{

    if( lsp->ltock[1] != 0 && --(lsp->ltock[1])==0 )
    {
	T20expired(lsp);	/* Restart request expired */
	return;
    }

    if( lsp->ltock[2] != 0 && --(lsp->ltock[2])==0 )
    {
	/* Expired */
	ltock2expired(lsp);     /* Connect or idle expiry */
	return;
    }

    if( lsp->ltock[3] != 0 && --(lsp->ltock[3])==0 )
    {
	/* Expired */
	random_expired(lsp);    /* Random timer expired */
	return;
    }
}


/************************************************************************/
static void xtock0expired(xvc)
/************************************************************************/

/*
    Either T21, 22, 23 or 26 has expired
*/

register struct x25vcformat FAR *xvc;

{
    if( xvc->xstate==P2 || xvc->xstate==P5 )    
    {
	/*
	    T21 - Call request timer
	    Action identical to protocol error
	*/
	L3monitor(t21_expire, xvc->xu_ident) ;
	ERR_rejU(xvc,(REMOTE_PROCEDURE_ERROR<<8)|49);
    }
    else if( xvc->xstate == WtgRCU || xvc->xstate == DXEresetting
	                           || xvc->xstate == RESUonly )
    {
	/*
	    T22 - Reset request timer
	    User error - no reset confirmation 
	*/
	L3monitor(t22_expire, xvc->xu_ident) ;
	xvc->T22_expire++;
	xusrerror(xvc,129);
    }
    else if( xvc->xstate==D2 || xvc->xstate==WtgRCN )
    {                               
	/*
	    T22 - Reset request timer
	    The procedure is continued with no state change, unless
	    R22 has been exceeded, and clear procedure entered
	*/
	cont_rst_procedure(xvc,0,51,xvc->xstate,xvc->xstate);
    }
    else if( xvc->xstate==P6 )
    {
	/*
	    T23 - Clear request timer
	    The clearing procedure is continued with no state change.
	    If R23 is exceeded, the 'cont' routine decides action
	*/
	L3monitor(t23_expire, xvc->xu_ident) ;
	cont_clr_procedure(xvc,0,50,P6,P6);
    }
    else if( (xvc->xstate==DataTransfer ||
              xvc->xstate==DXEbusy) && (xvc->xflag&INTOUTSTANDING)!=0 ) 
    {
	/*
	    T26 - Interrupt request timer
	    Enter the reset procedure incrementing rem_perr.
	*/
	L3monitor(t26_expire, xvc->xu_ident) ;
	xvc->T26_expire++;
	ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|145); 
    }
    else
    {
	L3monitor(L3badxtock0, xvc->xu_ident);
    }
}


/************************************************************************/
static void ack_expired(xvc)
/************************************************************************/

/*
    May send an acknowledgement, but the pool status is checked for
    crisis state.
*/

register struct x25vcformat FAR *xvc;

{

    if( xvc->xstate == DataTransfer || xvc->xstate == DXEbusy )
    {
	maybe_ack(xvc);  /* Which will start timer if pool low */
    }
    else
	L3monitor(L3badAE, xvc->xu_ident);
}


/************************************************************************/
static void T24expired(xvc)
/************************************************************************/

/*
    May require to update the remote window status
*/

register struct x25vcformat FAR *xvc;

{

    if( xvc->xstate == DataTransfer || xvc->xstate == DXEbusy )
    {
	/*
	    If no acknowledgments are pending on this circuit, and the
	    user has not acknowledged frames, then busy the circuit.

	    Unless however the unacknowledged frames are Dbit data 
	    requiring confs from the application. In this case the
	    window is not rotated.
	*/
	if( xvc->nrtogo != xvc->nrsent ) 
	{
	    /*
		Can acknowledge without busying
	    */
	    maybe_ack(xvc);

	    /*
		Start again if frames remain
	    */
	    if( xvc->ftoack != 0 )
		startT24(xvc);
	}
	else if( xvc->ftoack != 0 && xvc->Dreq_rcvd_cnt == 0 )
	{
	    /*
		Acknowledgment time expired. Reduce credit by
		number. Rotate the window
	    */
	    xvc->remcredit -= xvc->ftoack; 
#ifdef MOD8ONLY
	    xvc->nrtogo = ((xvc->nrsent + xvc->ftoack)&7);
#else
	    xvc->nrtogo = ((xvc->nrsent + xvc->ftoack)&xvc->xmodulo);
#endif
	    /*
		( ftoack will be 0 when the ack is sent )
	    */
	    maybe_ack(xvc);
	}
        /*
	    Otherwise, the clock is left stopped
	*/
    }

    else if( xvc->xstate == pDTransfer || xvc->xstate == pRESUonly ||
	                                  xvc->xstate == RESUonly )
    {
	startT24(xvc);		/* Just restart the clock */
    }

    else
	L3monitor(L3badT24, xvc->xu_ident);

}


/************************************************************************/
static void T25expired(xvc)
/************************************************************************/

/*
    No window rotation from remote
*/

register struct x25vcformat FAR *xvc;

{
    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy )
    {
	ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|146);
	L3monitor(L3T25timeouts, xvc->xu_ident);
	xvc->T25_expire++ ;
    }
    else
	L3monitor(L3badT25, xvc->xu_ident);
}


/************************************************************************/
static void T20expired(lsp)
/************************************************************************/

/*
    Restart timer expired
*/

register struct lsformat FAR *lsp;

{
    if( lsp->lstate==WtgRES || lsp->lstate==L3restarting ) 
    {
	/*
	    May send a DIAG here?
	*/
	if( lsp->lretx > lsp->lbase->R20value )
	{
	    /*
		Failed to establish the PLP, disconnect
	    */
	    L3monitor(l2r20exp, lsp->lu_ident);
	    dis_lsap_and_vcs( lsp, 0, 52 );      /* R20 exceeded */
	}
	else
	{
	    if( restart_request(lsp,0,52,NULL) )
	    {
		startT20(lsp);
		++lsp->lretx;
	    }
	    else
	    {
		lsp->lusrevent|=lneedbuf;
		lsp->lbureason =X_RES;
	    }
	}
	L3monitor(t20_expire, lsp->lu_ident) ;
    }
    else
	L3monitor(L3badT20, 0);
}


/************************************************************************/
static void ltock2expired(lsp)
/************************************************************************/

/*
    Connect or Idle expired
*/

register struct lsformat FAR *lsp;

{
    register struct x25vcformat FAR *temp;
    int k;

    if( lsp->lstate==L_connected )
    {
	/*
	    Idle timer running, not connect
	*/
	k=0;                      
	temp = lsp->CRqueue;

	/*
	    Check if we should restart timer
	*/
	while( temp != NULL )
	{
	    if( temp->NSUattached != 0 )
	    {
		k = 1;     /* User is attached, no disconnect */
		break;
	    }
	    else
		temp = temp->xvclink;
	}

	if( k == 0 )
	{
	    /*
		Start disconnection with 'link gone' diagnostic.
	    */
	    dis_lsap_and_vcs( lsp, 0, 128 );  
	}
	else
	    /*
		Else just restart timer
	    */
	    start_idle_timer(lsp);

    }
    else if( lsp->lstate==L_connecting ||
             lsp->lstate==WtgDXE || lsp->lstate==WtgRES )
    {
	/*
	    Connect timer expired.
	    Failed to set up the link level. Disconnect with 'link gone'
	    diagnostic.
	*/
	L3monitor(l2dxeexp, lsp->lu_ident);
	dis_lsap_and_vcs( lsp, 0, 128 );
    }
    else
	L3monitor(L3badltock2, 0);
}


/************************************************************************/
static void random_expired(lsp)
/************************************************************************/

/*
    Random timer expired. Used for restart generation
*/

register struct lsformat FAR *lsp;

{
    if( lsp->lstate==WtgDXE )
    {
	if( restart_request(lsp,0,0,NULL) )
	{
	    lsp->lstate=WtgRES;
	    lsp->lretx=1;
	    startT20(lsp);
	}
	else
	{
	    /*
		No buffers so better clear down. Use
		diagnostic 'congestion'.
	    */
	    L3monitor(l2dxebuf, lsp->lu_ident);
	    dis_lsap_and_vcs( lsp, 0, 163 );
	}
    }
    else
	L3monitor(L3badrandom, 0);
}
