#ident "$Id: x25rxin.c,v 1.7 1996/11/21 22:06:55 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
 *
 *  X25RXIN.C
 *
 *
 *******************************************************************/

/*
 *	x25rxin.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25rxin.c
 *	@(#)x25rxin.c	1.41
 *
 *	Last delta created	15:52:36 4/20/92
 *	This file extracted	18:33:03 11/19/92
 *
 *
#endif
 */

#include "x25_inc.h"

#include "x25pti.h"
#include "x25cause.h"
#include "x25clock.h"
#include "x25form.mac"
#include "x25fac.h"
#include "x25mbu.h"


#ifndef NULL
#define	NULL 0
#endif

extern unsigned long curtime();
extern queue_t *SNMP_trace_q ;

/************************************************************************/
void Rx_CALL(lsp,xvc,gfi,inpkt)
/************************************************************************/

/*
    Rx_CALL - handle incoming call indications

    Incoming call to handle.  Only two states are important here -
    P1 and P2.  The call collision state P5 is only entered if this
    DTE is maintaining it's role as a DTE, otherwise -see ISO 5.2.5-
    both calls are cleared.
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
unsigned char gfi;
mblk_t *inpkt;

{

    struct x25vcformat xvc_temp;

    int d, Abitin;

#ifdef DEBUG      
    strlog(X25_STID,xvc->xu_ident,1,SL_TRACE,
           "CALL  In  : '%x' lci %03x gfi %x",xvc->xu_ident,xvc->lci,gfi>>4);
#endif
		   
    if( xvc->xstate==P1 || xvc->xstate==P2 )
    {
	if( xvc->xstate==P2 )
	{
	    /*
		If DTE and call has no errors enter call collision
		state, otherwise clear.
	    */
	    if( lsp->DTEmode==0 )
	    {
		L3monitor(rjc_coll, xvc->xu_ident);
		stop_vc_timers(xvc);
		disconnect_user(xvc,0,72,(mblk_t *)NULL); 
		into_clr_procedure(xvc,0,72,P6,P6pending);
	    }
	    else
	    {

		Abitin = ((gfi&0x80) != 0);
		/*
		    Save details of original call
		*/
		bcopy((bufp_t)xvc,
		      (bufp_t)&xvc_temp,sizeof(struct x25vcformat));

		/*
		    Check packet format
		*/
		d = pkt_details(lsp,xvc,X_CALL,inpkt,Abitin);   

		/*
		    Restore details of original call
		*/
		bcopy((bufp_t)&xvc_temp,
		      (bufp_t)xvc,sizeof(struct x25vcformat)); 
	    
		if( d == 0 )                      /* Call OK             */
	        {
		    /*
			Wait for response from the DCE
		    */
		    L3monitor(cll_coll, xvc->xu_ident);
		    xvc->xstate=P5;
		}
		else			       /* Format error so clr */
	        {
		    stop_vc_timers(xvc);
		    disconnect_user(xvc,REMOTE_PROCEDURE_ERROR,(unsigned char)(d&255),(mblk_t *)NULL);
		    into_clr_procedure(xvc,0,(unsigned char)(d&255),P6,P6pending);
		}
	    }
	}
	else   /* State P1 */
	{
	    Abitin = ((gfi&0x80) != 0);
	    xvc->rem_Dsupport = ((gfi&0x40) != 0);

	    if(( xvc->xdomain == WAN84 || xvc->xdomain == WAN88 ) &&
	        (xvc->xbase->SUB_MODES & AND_80) != 0 )
	    {
		xvc->xdomain  = WAN80; /* Until overridden */
	    }

	    if( (d=pkt_details(lsp,xvc,X_CALL,inpkt,Abitin))==0 )
	    {   
		/* NB map_tclass called in pkt_details */
		fix_def_facilities(xvc,X_INWARD);   /* To fix defaults etc. */

		if( (d=qos_available(xvc,X_INWARD)) != 0 )
		    L3monitor(L3qoscheck, xvc->xu_ident);

		/*
		    Check if admin has barred incalls
		*/
		if( d==0 && (xvc->xbase->SUB_MODES&BAR_INCALL) != 0 )
		    d = 70;
	    }

	    if( d==0 )
	    {
                L3monitor(cll_in, xvc->xu_ident);

		xvc->NSUattached=1;          /* For proper disc handling */
		xvc->xstate = P3;            /* Enter DXE calling        */
		xvc->qposition = 0;          /* Start of search for listener */
		xvc->est_time = curtime();   /* SNMP: Establish time     */
		xvc->direction = X25_DRCTN_IN; /* SNMP: Establish direction */
		CI_touser(xvc,mb_frag(inpkt));      /* NSAP check & indication  */
	    }
	    else   /* Bad call details */
	    {
		ERR_clrnoU(xvc,d);
	    }
	}
    }
    else   /* Bad state for call */
	state_matrix(xvc,X_CALL);
}

/************************************************************************/
void Rx_CAA(lsp,xvc,gfi,inpkt)
/************************************************************************/

/*
    Rx_CAA - handle incoming call confirmation
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
unsigned char gfi;
mblk_t *inpkt;

{
    unsigned char dd;
    int           d, Abitin;

    struct wlcfg *xx;

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,1,SL_TRACE,
           "CAA   In  : '%x' lci %03x gfi %x",xvc->xu_ident,xvc->lci,gfi>>4);
#endif

    if( xvc->xstate==P2 || xvc->xstate==P5 )
    {
	xx =xvc->xbase;
	stopT21(xvc);

	if( (xvc->ISOflag&FSRRESP)==0 ) 
	{
	    Abitin = ((gfi&0x80) != 0);
	    if( (d=pkt_details(lsp,xvc,X_CAA,inpkt,Abitin))==0 )
	    {
		dd=((gfi&0X40) != 0);

		/*
		    Dbit ok if not set, or set and local support, or no local
		    support but non cons and control info doesnt clear.
		*/
		if( !dd || xvc->loc_Dsupport
	                || ( !xvc->CONS_call 
		             && !(xx->psdn_local.dbit_control&CC_IN_CLR)) )
		{
                    L3monitor(caa_in, xvc->xu_ident);
		    xvc->rem_Dsupport=dd;

		    /*
		       Enter, no flow and pass confirm up to user.
		       Mark as opened
		    */
		    to_P4D1(xvc,0);
		    CC_touser(xvc,mb_frag(inpkt)); 
		    xvc->xflag |= (caareq|vc_counted);

		    /*
			Increment vc count on lsap and since active
			stop idle timer
		    */
		    if( (lsp->concount++) == 0)
			stop_idle_timer(lsp);  
		}
		else                       
		{
		    /*
			Unauthorised D-bit. Strictly not an error
			if can support D-bit (see CCITT)
		    */
		    ERR_rejU(xvc,166);
		}
	    }
	    else
	    {
		/*
		    Bad fac. or other
		*/
		ERR_rejU(xvc,d);
	    }
	}
	else    
	{

			printf("42-30") ;
		/*
		CAA to FSRRESP call
	    */
	    ERR_rejU(xvc,(REMOTE_PROCEDURE_ERROR<<8)|42);/* PTI incomp. with facil. */
	}
    }
    else
    {
	state_matrix(xvc,X_CAA);
    }
}


/******************************************************************/
void Rx_RST(xvc,len,inpkt)
/******************************************************************/

register struct x25vcformat FAR *xvc;
int len;
mblk_t *inpkt;
{
    unsigned char cause, diag;

    /*
	Bump length if required
    */
    if( len==1 && (xvc->xbase->psdn_local.SNMODES&ACC_NODIAG) )
        len=2; 

    (void) mb_getch(xvc,(bufp_t)&cause);
    (void) mb_getch(xvc,(bufp_t)&diag);

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,3,SL_TRACE,"RST   In  : '%x' lci %03x C/D %04x",
                                        xvc->xu_ident,xvc->lci,cause<<8|diag);
#endif

    m_freem(mb_frag(inpkt));  /* Retain header in case of RSC */

    if( len!=2 && (xvc->xstate>=DataTransfer && xvc->xstate<=DXEresetting) )
    {
	/*
	    That is, packet is acceptable but contains a format error
	    such errors are counted in the P4 routine
	*/
	P4_FMT_error( (struct x25vcformat FAR *)xvc, (unsigned char) (len<2 ? 38: 39) );
	m_freem(inpkt);
	return;
    }

    L3monitor(rst_in, xvc->xu_ident);
    if (!cause || (cause & 0x80)) {
	L3monitor(remote_rst, xvc->xu_ident) ;
	xvc->remote_resets++ ;
    } else {
	L3monitor(provider_rst, xvc->xu_ident) ;
	xvc->provider_resets++ ;
    }

    if (SNMP_trace_q) SNMP_reset_event(xvc) ;

    if (((cause & 0x80) == RREMOTE_PROCEDURE_ERROR) ||
	((cause & 0x80) == RLOCAL_PROCEDURE_ERROR))
	L3monitor(prc_errors, xvc->xu_ident) ;

    switch( xvc->xstate )
    {
    case DataTransfer:
    case DXEbusy:
as_dt:					/* Shared code entry */

	stop_vc_timers(xvc);
	tidy_user_side(xvc);                    /* Free resource etc B4 user */

	if( reset_user(xvc,cause,diag) )        /* Reset indication sent?   */
	{
	    xvc->xstate=DXEresetting;     /* To await user response */
	    startT22(xvc);
	}
	else
	{
	    xvc->xstate=DXErpending;
	    need_buffer(xvc,Usr_Req);
	}
	
    break;

    case D2:

	/*
	    Enter data transfer, send flow
	*/
	stopT22(xvc);
	to_P4D1(xvc,1);
	
    break;

    case D2pending:

	/*
	    Reset was pending but not yet sent.  Use
	    this collision as the resync. required
	*/
asD2pending:                              /* Entry from other pending states */

	xvc->xusrevent&= ~xBUFreq;        /* Cancel buffer request */
	if( short_out(xvc,RSC_PTI) )
	{
	    to_P4D1(xvc,1);               /* Enter data transfer with flow */
	}
	else
	{
	    need_buffer(xvc,X_RSC);
	    xvc->xstate=P4pending;        /* Mark going P4 next */
	}
	
    break;

    case P4pending:

	/*
	    RSC pending but not yet sent.  Try to confirm again.
	*/
	goto asD2pending;                    /* Action is the same */


    case WtgRCN:

    	/*
	    Take collision as reset confirmation. Enter data transfer
	    no flow required.
	*/
	stopT22(xvc);
	to_P4D1(xvc,0);
	reset_confirm_user(xvc,inpkt);
	inpkt=NULL;
	
    break;

    case WtgRCNpending:

	/*
	    Trying to reset, but no buffers.  Take this RST as the
	    required resync
	*/
	reset_confirm_user(xvc,inpkt);
	inpkt=NULL;
	goto asD2pending;                    /* Action is the same */


    case DXEresetting:
    case DXErpending:

	/*
	    Assume repeat, do nothing.
	*/

    break;

    case WRCUpending:          

	/*
	    Was trying internal reset. 	Note: bureason is the same
	*/
	xvc->xstate=DXErpending;
	
    break;

    case WtgRCU:

	/*
	    Internal reset started. But still wait for user.
	*/
	xvc->xstate=DXEresetting;
	
    break;


    case pDTransfer:

	/*
	    Use buffer for RSC to user and reset then drop through
	    the pRESUonly code
	*/
	reset_confirm_user(xvc,inpkt);
	inpkt=NULL;
			
    case pRESUonly:

	/*
	    Delete any waiting data and proceed with the network
	    reset
	*/
	del_sqrl_data(xvc);	
	xvc->xusrevent&= ~xBUFreq;
	goto as_dt;		

    case RESUonly:

	/*
	    Abandon RESU and reset. Delete any waiting data
	    and drop INT flags etc. 
	*/
	del_sqrl_data(xvc);	
	tidy_user_side(xvc);
	xvc->xstate = DXEresetting;
	
    break;


    default:

	state_matrix(xvc,X_RST);
	
    break;

   }

   if ( inpkt != NULL )
	m_freem(inpkt);
}


/******************************************************************/
void Rx_RSC(xvc,len,inpkt)
/******************************************************************/

/*
    Handle incoming reset confirmation
*/

register struct x25vcformat FAR *xvc;
int len;
mblk_t *inpkt;

{
#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,3,SL_TRACE,"RSC   In  : '%x' lci %03x",
                                                 xvc->xu_ident,xvc->lci);
#endif
    m_freem(mb_frag(inpkt));    /* Retain header for RSC */

    if( xvc->xstate==D2 || xvc->xstate==WtgRCN )
    {
	stopT22(xvc);
	if( len==0 )
	{
	    if( xvc->xstate==WtgRCN )
	    {
		xvc->xstate=DataTransfer;  /* In case tx */
		reset_confirm_user(xvc,inpkt);
		inpkt=NULL;
	    }
	    to_P4D1(xvc,1);                /* Enter data transfer, with flow */
	}
	else                               /* RSC format error */
	{
	    P4_FMT_error((struct x25vcformat FAR *)xvc,39);
	}
    }
    else
    {
	state_matrix(xvc,X_RSC);
    }

    if ( inpkt != NULL )
	m_freem(inpkt);
}


/******************************************************************/
void Rx_REJ(xvc)
/******************************************************************/

/*
    Handle incoming REJect packet. Currently NOT SUPPORTED.
*/

struct x25vcformat FAR *xvc;
{
    state_matrix(xvc,X_REJ);
}


/******************************************************************/
void Rx_RNR(xvc,pti,len)
/******************************************************************/

/*
    Handle incoming RNR packet
*/

register struct x25vcformat FAR *xvc;
unsigned char pti;
int len;

{
    unsigned char nr;
    int packd;
#ifndef MOD8ONLY
    int e_seq;
#endif

    int dacked;
		
#ifndef MOD8ONLY
    if( xvc->xmodulo == 127 )
    {
	e_seq = 1;
	(void) mb_getch(xvc,(bufp_t)&nr);
	if( (nr&1) != 0 )        /* Bottom bit non zero - X_BADnnPTI */
	{
	    state_matrix(xvc,X_BADnnPTI);
	    return;
	}
	len--;
    }
    else
	e_seq = 0;
#endif

    /*
	For PVC pending states, the RNR is ignored.
    */
    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy ||
        xvc->xstate == pRESUonly  || xvc->xstate == RESUonly ||
	xvc->xstate == pDTransfer )
    {
	if( len != 0 )
	{
	    P4_FMT_error((struct x25vcformat FAR *)xvc,(unsigned char)(len>0 ? 39 : 38)); 
	    return;
	}

#ifdef MOD8ONLY
	nr=(pti>>5);
	packd=(nr-xvc->lastnr)&7;               /* No. packets acknowledged */
#else
	if( e_seq == 0 )
	{
	    nr=(pti>>5);
	    packd=(nr-xvc->lastnr)&7;           /* No. packets acknowledged */
	}
	else
	{
	    nr = (nr>>1)&127;
	    packd=(nr-xvc->lastnr)&127;         /* No. packets acknowledged */
	}
#endif

	if( (xvc->acksoutstanding-=packd)>=0 )  /* I.e. within my window    */
	{
	    L3monitor(rnr_in, xvc->xu_ident);

	    /*
		? Clocks - implement a busy clock ?
	    */
	    if( packd>0 )
	    {
		/*
		    Check if the window has advanced enough to acknowledge
		    any outstanding Dbit packets. If so sent ack to user.
		    Note DA_touser may fail resulting in the call being
		    cleared
		*/
		if ( xvc->Dreq_sent_cnt )
		{
		    dacked = Dsent_acked(xvc,nr);
		
		    for ( ; dacked > 0 ; dacked-- )
		    {
			xvc->Dreq_sent_cnt--;
			DA_touser(xvc);
		    }
		}

		xvc->lastnr=nr;
		if( xvc->acksoutstanding ) startT25(xvc);
		else                        stopT25(xvc);
	    }

	    if( xvc-> xstate==DataTransfer )
	    {
		/*
		    Here is where a busy timer may be started
		*/
		xvc->xstate=DXEbusy;
	    }
	    /*
		No data nor flow started, INT/INC picked up on exit
	    */
	}
	else 
	{
	    /*
		Invalid P(R)
	    */
	    ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|2);
	}
    }
    else
    {
	state_matrix(xvc,X_RNR);
    }
}


/******************************************************************/
void Rx_RR(xvc,pti,len)
/******************************************************************/

/*
    Handle incoming RR packet
*/

register struct x25vcformat FAR *xvc;
unsigned char pti;
int len;

{
    unsigned char nr;
    int packd;
#ifndef MOD8ONLY
    int e_seq;
#endif
    int dacked;
		

#ifndef MOD8ONLY
    if( xvc->xmodulo == 127 )
    {
	e_seq = 1;
	(void) mb_getch(xvc,(bufp_t)&nr);

	if( (nr&1) != 0 )        /* Bottom bit non zero - X_BADnnPTI */
	{
	    state_matrix(xvc,X_BADnnPTI);
	    return;
	}
	len--;
    }
    else
	e_seq = 0;
#endif

    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy ||
        xvc->xstate == pRESUonly  || xvc->xstate == RESUonly ||
	xvc->xstate == pDTransfer )
    {
	if( len != 0 )
	{
	    P4_FMT_error((struct x25vcformat FAR *)xvc,(unsigned char) (len>0 ? 39 : 38));
	    return;
	}

#ifdef MOD8ONLY
	nr=(pti>>5);
	packd=(nr-xvc->lastnr)&7;            /* No. packets acknowledged */
#else
	if( e_seq == 0 )
	{
	    nr=(pti>>5);
	    packd=(nr-xvc->lastnr)&7;        /* No. packets acknowledged */
	}
	else
	{
	    nr = (nr>>1)&127;
	    packd=(nr-xvc->lastnr)&127;      /* No. packets acknowledged */
	}
#endif

	if( (xvc->acksoutstanding-=packd)>=0 )
	{
	    L3monitor(rr_in, xvc->xu_ident);
	    /*
		The window may now be open for more user data
	    */
	    if( xvc->xstate == DataTransfer )	/* Most of the time */
	    {
		/* Please do nothing */
	    }
	    else if( xvc->xstate==DXEbusy )
	    {
		/*
		    Here is where to stop a busy timer
		*/
		xvc->xstate=DataTransfer;
	    }
	    else	                        /* PVC states - nothing */
		return;
	    if( packd>0 )
	    {
		/*
		    Check if the window has advanced enough to acknowledge
		    any outstanding Dbit packets. If so sent ack to user.
		    Note DA_touser may fail resulting in the call being
		    cleared
		*/
		if ( xvc->Dreq_sent_cnt)
		{
		    dacked = Dsent_acked(xvc,nr);
		    for (; dacked > 0 ; dacked-- )
		    {
			xvc->Dreq_sent_cnt--;
			DA_touser(xvc);
		    }
		}
		
		xvc->lastnr=nr;
		
		if( xvc->acksoutstanding ) startT25(xvc);
		else                        stopT25(xvc);
	    }

	    /*
		Try to send data, but only if no INT/INC.
		Which may also send flow
	    */
	    if( (xvc->xusrevent & (xINTout|xINCout)) == 0 )
		Tx_Qdata(xvc);
	    else
	    {
		/*
		    If necessary, remake the data event for later
		*/
		if( xvc->acksoutstanding < (int)xvc->lwsize &&
		   (xvc->x25txqsize != 0 || xvc->datatogo != NULL) )
		{
		    xvc->xusrevent|=xDTout;
		}
	    }
	    
	    /*
		Deal with flow (user may Tx) - NB no packd check
		(busy perhaps)
	    */
	    if( xvc->Udatawaiting &&
	       ((unsigned int)(xvc->acksoutstanding+xvc->x25txqsize)
			<(unsigned int)xvc->lwsize) )
	    {
		xvc->Udatawaiting=0;
		FL_touser(xvc);
	    }
	}
	else
	{
	    /*
		Invalid P(R)
	    */	
	    ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|2);
	}
    }
    else
    {
	state_matrix(xvc,X_RR);
    }
}


/******************************************************************/
void Rx_INC(xvc,len,inpkt)
/******************************************************************/

/*
    Handle incoming interrupt confirmation. PVC states INC is not allowed.
*/

register struct x25vcformat FAR *xvc;
int len;
mblk_t *inpkt;

{

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,5,SL_TRACE,"INTC  In  : '%x' lci %03x",xvc->xu_ident,xvc->lci);
#endif
    
    /*
	Note no check on format error
    */
    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy )
    {
	if( (xvc->xflag & INTOUTSTANDING) != 0 )
	{
	    if( len != 0 )
	    {
		P4_FMT_error((struct x25vcformat FAR *)xvc,39);
		m_freem(inpkt);
		return;
	    }
	    stopT26(xvc);

	    /*
		Drop flag and confirm to user with reuse of the header
	    */
	    xvc->xflag&= ~INTOUTSTANDING;
	    m_freem(mb_frag(inpkt));
	    EDA_touser(xvc,inpkt);
	    inpkt=NULL;
	}
	else
	{
	    ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|43);
	}
    }
    else
    {
	state_matrix(xvc,X_INC);
    }

    if ( inpkt != NULL )
	m_freem(inpkt);
}


/******************************************************************/
void Rx_INT(xvc,len,inpkt)
/******************************************************************/

/*
    Handle incoming interrupt indication
*/

register struct x25vcformat FAR *xvc;
int len;
mblk_t * inpkt;

{
    int expallowed;

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,5,SL_TRACE,"INT   In  : '%x' lci %03x",xvc->xu_ident,xvc->lci);
#endif

    xvc->rx_int_pkts++ ;

    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy ||
        xvc->xstate == pRESUonly  || xvc->xstate == RESUonly ||
	xvc->xstate == pDTransfer )
    {
	/*
	    WANLAN: difference in status between 1984 and 1980
	*/
	if( (xvc->xflag&INCwaited) == 0 )
	{
	    expallowed = ((xvc->xdomain == WAN80) ? 1 :
	                  (xvc->CCITTflag&EXPINUSE) != 0  ? 1 : 0 );
	}
	else
	    expallowed = 0;

	/*
	    WANLAN: different lengths allowed 1984 and 1980
	*/
	if( expallowed != 0 && len>0 && len<33 &&
	    ((xvc->xdomain==WAN80 && len==1) || xvc->xdomain!=WAN80) )
	{
	    L3monitor(ed_in, xvc->xu_ident);
	    if( xvc->xstate != DataTransfer && xvc->xstate != DXEbusy )
	    {
		/*
		    Hide the data away on the glue
		*/
		sqrl_PVC_data(xvc,inpkt,1);	/* Expedited */
	    }
	    else
		ED_touser(xvc,inpkt);
	    inpkt=NULL;
	    xvc->xflag |= INCwaited;

	    /*
		And go off to await the user's acknowledgement
	    */
	}
	else                                  /* Some error */
	{
	     /*
		Check which error has occurred either unauthorised,
		short or long.
	     */
	    if( expallowed == 0 )
		ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|44);
	    else
		P4_FMT_error((struct x25vcformat FAR *)xvc, (unsigned char) (len==0 ? 38 : 39) );
	}
    }
    else
    {
	state_matrix(xvc,X_INT);
    }

    if ( inpkt != NULL )
	m_freem(inpkt);
}

/************************************************************************/
void Rx_CLR(lsp,xvc,gfi,len,inpkt)
/************************************************************************/

/*
    Handle incoming clear indication
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
int len;
mblk_t *inpkt;
unsigned char gfi;

{
    unsigned char cause, diagnostic;
    int d,Abitin;
    mblk_t *dat;
    struct wlcfg *xx;

    xx =xvc->xbase;

    /*
	Check for format error
    */
    if( len==1 && (xx->psdn_local.SNMODES&ACC_NODIAG) )
	len=2;  /* Bump length */

    if( len<2 )
	d=(REMOTE_PROCEDURE_ERROR<<8)|38;
    else
    {
#ifdef DEBUG
        printf("SNMODE state for CLEAR_LEN = %x \n",xx->psdn_local.SNMODES&CCITT_CLEAR_LEN);
#endif
	if( (len > 2 && xvc->xdomain==WAN80) &&   
	   ((xvc->ISOflag&FSELECT)==0 || (xvc->xflag&caareq)!=0) &&
	    (xx->psdn_local.SNMODES&CCITT_CLEAR_LEN) )
	{
	    /*
		Extended format CLR. For 1980 this is only used for cudf and
		is hence only valid in conjunction with Fast Select and if
		in call setup state.
		The exception is for networks which send extended Clear Indications
		for non-Fast Select calls.  eg. PSS for Call Statitistics.
		Checked here to produce correct diagnostic.
	    */
	    d=(REMOTE_PROCEDURE_ERROR<<8)|39;
#ifdef DEBUG
printf("rpc 1 here \n");
#endif
	}
	else
        {
	    (void) mb_getch(xvc,(bufp_t)&cause);
	    (void) mb_getch(xvc,(bufp_t)&diagnostic);
	    Abitin = ((gfi&0x80) != 0);
	    if( (d=pkt_details(lsp,xvc,X_CLR,inpkt,Abitin))==0 )
	    {
		len=mb_bulen(inpkt);
		if( len==0 )
		{
		    m_freem(inpkt);
		    inpkt=NULL;
		}
		else if( len>128 ||     /* Check cudf validity */
		     (len!=0 && (xvc->ISOflag&FSELECT)==0) )
		    d=(REMOTE_PROCEDURE_ERROR<<8)|39;
	    }
	}
    }

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,2,SL_TRACE,
           "CLR   In  : '%x' lci %03x C/D %04x",xvc->xu_ident,xvc->lci,cause<<8|diagnostic);
#endif

    /*
	The user process is disconnected, even in the presence of a format
	error in the packet.  In the latter case, any user disconnect data
	and diagnostics are freed and overwritten respectively.
    */
    if( d!=0 )
    {
	L3monitor(rem_perr, xvc->xu_ident);
	m_freem(inpkt);
	inpkt=NULL;
	cause=REMOTE_PROCEDURE_ERROR;
	diagnostic=(d&255);
    }

    /*
     * Is the clear DTE originated?
     */
    if (!cause || (cause & 0x80))
	L3monitor(pinitclears, xvc->xu_ident);

    /*
     * Does the clear involve local/remote procedure error?
     */
    if ((cause & 0x7D) == REMOTE_PROCEDURE_ERROR)
	L3monitor(prc_errors, xvc->xu_ident);

    if( inpkt != NULL )
    {
	dat = (mblk_t *) mb_frag(inpkt);
	m_freem(inpkt);
	inpkt=NULL;
    }
    else
	dat = NULL;

    switch( xvc->xstate )
    {

    case P2:
    case P5:      

	/*
	    Strictly speaking, a reject_user. Monitor and
	    drop thru
	*/
	if( d==0 ) L3monitor(rjc_remote, xvc->xu_ident);

    case P3:
    case DataTransfer:
    case DXEbusy:
    case D2:
    case D2pending:
    case WtgRCU:
    case WRCUpending:
    case WtgRCN:
    case WtgRCNpending:
    case P4pending:
    case DXErpending:
    case DXEresetting:			/* Disconnect */
    case WUNcpending:			/* Try to indicate this time */
#ifndef INTERNAL_DISC
    case P6pending:                                
#endif
as_wunc:				/* (Error entry from DXEcpending) */

	xvc->xusrevent &= ~xBUFreq;
	stop_vc_timers(xvc);
	if( disconnect_user(xvc,cause,diagnostic,(mblk_t *)dat) )
	{
	    xvc->xstate=DXEclearing;
	    dat = NULL;
	    goto clresp;
	}
	else
	{
	    /*
		Any user data is stored in Udiscdata of the db
	    */
	    dat=NULL;
	    if( d==0 )			/* Packet was not in error */
	        xvc->xstate=DXEcpending;
	    else
		xvc->xstate=WUNcpending;
	    need_buffer(xvc,Usr_Req);
	}
	
    break;

#ifndef INTERNAL_DISC
    case DXEcfpending:
#endif
    case DXEcpending:		/* Assume retransmission */

	if( d != 0 )		/* or handle as an error */
	    goto as_wunc;
	    
    break;

    case P1:
    case DXEclearing:
#ifdef INTERNAL_DISC
    case P6pending:                     /* No user, confirm */
#endif

clresp:
	if( d==0 )
	{
	    if( short_out(xvc,CLC_PTI)!=0 )
	    {
		dec_ls_concount(lsp,xvc);
		xfreedb(xvc);                 /* Setting xstate idle */
	    }
	    else
	    {
		xvc->xstate=DXEclearing;
		need_buffer(xvc,X_CLC);
	    }
	}
	else
	    goto clrfmterr;                   /* Format error */

        break;

clrfmterr:

	into_clr_procedure(xvc,0,diagnostic,P6,P6pending);
	
    break;

    case P6:                                  /* Collision if no error */

	if( d==0 )
	{
#ifndef INTERNAL_DISC	    
	    if ( disc_confirm_user(xvc) )
	    {
		dec_ls_concount(lsp,xvc);
		xfreedb(xvc);                  /* Setting xstate idle */
	    }
	    else
	    {
		xvc->xstate=DXEcfpending;
		need_buffer(xvc,X_CLC);
	    }
#else
	    dec_ls_concount(lsp,xvc);
	    xfreedb(xvc);                      /* Setting xstate idle */
#endif
	}
	else
	{
	    /* To avoid loops of clr/badclr, just continue the clr */

	    cont_clr_procedure(xvc,0,diagnostic,P6,P6);
	}
	
    break;

    default:

	system_error(L3clrbadstate);
	
    break;

    }

    /* Catch all for the clear user data */

    if( dat != NULL )
	m_freem(dat);

}

/************************************************************************/
void Rx_CLC(lsp,xvc,gfi,len,inpkt)
/************************************************************************/

/*
    Handle incoming clear confirmation
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
int len;
mblk_t *inpkt;
unsigned char gfi;
{
    int      d,Abitin;

#ifdef DEBUG
    strlog(X25_STID,xvc->xu_ident,2,SL_TRACE,"CLC   In  : '%x' lci %03x",
                                                 xvc->xu_ident,xvc->lci);
#endif

    /*
	Here we have to consider the effect of error detection.  If a CLC
	is repeatedly in error, then this will tie up resources when R23
	count expires
    */
    if( xvc->xstate==P6 )
    {
        d=0;
#ifdef DEBUG
        printf("2:SNMODE state for CLEAR_LEN = %x \n",xvc->xbase->psdn_local.SNMODES&CCITT_CLEAR_LEN);
#endif
        if( len > 0 && xvc->xdomain==WAN80
		    && xvc->xbase->psdn_local.SNMODES&CCITT_CLEAR_LEN)
        {
	    d=(REMOTE_PROCEDURE_ERROR<<8)|39;
#ifdef DEBUG
printf("RPC 2 here\n");
#endif
	}
	else
	{
	    Abitin = ((gfi&0x80) != 0);
	    d=pkt_details(lsp,xvc,X_CLC,inpkt,Abitin);
	}

	/* Now action depends on whether an error was detected */
	if( d==0 )
	{
#ifndef INTERNAL_DISC
	    if ( disc_confirm_user(xvc) )
	    {
	        dec_ls_concount(lsp,xvc);
	        xfreedb(xvc);               /* Setting xstate idle */
	    }
	    else
	    {
	        xvc->xstate=DXEcfpending;
	        need_buffer(xvc,X_CLC);
	    }
#else
	    dec_ls_concount(lsp,xvc);
	    xfreedb(xvc);               /* Setting xstate idle */

#endif
	}
	else
	{
	    L3monitor(rem_perr, xvc->xu_ident);
	    cont_clr_procedure(xvc,0,(unsigned char)(d&255),P6,P6);
	}
    }
    else
    {
	state_matrix(xvc,X_CLC);
    }
}


/************************************************************************/
void Rx_DATA(xvc, gfi, pti, len, inpkt)
/************************************************************************/

/*
    Handle incoming data indication. Reassemble (up to a limit) if necessary.
*/

register struct x25vcformat FAR *xvc;
unsigned char gfi, pti;
int len;
mblk_t *inpkt;

{
    mblk_t *temp;
    unsigned char Qbitin, Mbitin, Dbitin, nr, ns, diag;
    int packd, sendin;
    int dacked;
		

    /*
	Check current state of SVC or PVC is valid for receiving data
    */
    if( xvc->xstate == DataTransfer || xvc->xstate == DXEbusy  ||
        xvc->xstate == pRESUonly    || xvc->xstate == RESUonly ||
	xvc->xstate == pDTransfer )
    {
	/*
	    Obtain the Mbit, P(R) and P(S) for this packet, taking care
	    of the different formats depending on the modulo.
	*/
#ifdef MOD8ONLY
	Mbitin = (pti&0X10);
	nr = (pti>>5);
	ns = ((pti>>1)&7);
#else
	if( xvc->xmodulo == 127 )
        {
	    /*
		P(R) contained in next byte
	    */
	    if ( mb_getch(xvc,(bufp_t)&nr) == 0 )
	    {
		diag = 38;      /* Short */
		goto daterr;
	    }
	    len--;
	    
	    Mbitin = ((nr&1) ? 0x10 : 0x00 ); 
	    nr = (nr>>1);
	    ns = (pti>>1);
        }
	else
        {
	    Mbitin = (pti&0X10);
	    nr = (pti>>5);
	    ns = ((pti>>1)&7);
        }
#endif
/* CDK
	strlog(X25_STID,xvc->xu_ident,6,SL_TRACE,
	       "DATA  In  : '%x' lci %03x ns/nr %04x",xvc->xu_ident,xvc->lci,ns<<8|nr);
*/
	/*
	    Examine the gfi for Q and D-bits.  
	*/
	Qbitin=(gfi&0X80);
	Dbitin=(gfi&0X40);

	/*
	    Check compatibility of Dbit setting with Dbit control
	    configuration, and rx data length against packet length in use.
	*/
	if( (!Dbitin || !(xvc->Dctl&DT_IN_RST)) && (len <= (int)xvc->rpkt) )
	{
	    /*
		Check compatibility of Qbit setting with the last
		unless this packet is still part of a NSDU.
	    */
	    if( xvc->lastMin == 0 || (Qbitin == xvc->lastQin) )
	    {
		/*
		    Frame may carry acknowledgements, check window
		    and validity of NR
		*/
#ifdef MOD8ONLY
		packd = ( nr - xvc->lastnr )&7;
#else
		packd = ( nr - xvc->lastnr )&xvc->xmodulo;
#endif
		if( (xvc->acksoutstanding -= packd) >= 0 )
		{
		    /*
			Valid NR. If NR indicates frames acked then stop
			window rotation timer. If still frames outstanding
			then restart it.
		    */
		    if( packd > 0 )
		    {
			/*
			    Check if the window has advanced enough to
			    acknowledge any outstanding Dbit packets.
			    If so sent ack to user. Note DA_touser may fail
			    resulting in the call being	cleared.
			*/
			if ( xvc->Dreq_sent_cnt)
			{
			    dacked = Dsent_acked(xvc,nr);
			    for ( ; dacked > 0 ; dacked-- )
			    {
				xvc->Dreq_sent_cnt--;
				DA_touser(xvc);
			    }
			}

			if( xvc->acksoutstanding )
			    startT25(xvc);
		    	else
			    stopT25(xvc);
		    }

		    /*
			Check NS is expected and within the window
		    */
		    if( ns == xvc->psexpected && ++xvc->ftoack <= xvc->rwsize )
		    {
		    	/*
			    If Mbit set then unless the Dbit is also set
			    the packet must be full.
			*/
			if( !Mbitin || Dbitin || (len == xvc->rpkt) )
			{
			    L3monitor(dt_in, xvc->xu_ident);
			    xvc->in_octets += len ;
			    xvc->in_pdu++ ;

			    /*
				Store last settings for flags
			    */
			    xvc->lastQin = Qbitin;
			    xvc->lastMin = Mbitin;
			    xvc->lastnr  = nr;
#ifdef MOD8ONLY
			    xvc->psexpected =(xvc->psexpected + 1)&7;
#else
			    xvc->psexpected =(xvc->psexpected + 1)&xvc->xmodulo;
#endif
			    /*
				Start the window status timer if it
				wasnt already running.
			    */
			    if( xvc->ftoack == 1 ) startT24(xvc);

			    /*
				If already in the process of reassembling a
				nsdu, then carry on regardless of setting of
				Mbit.
			    */
			    if( xvc->reassemble!=NULL )
			    {
				/*
				    If data length > 0 then extract the data
				    block from the message and concatenate to
				    existing NSDU.
				*/
				if( len > 0 )
				{
				    temp = (mblk_t *) mb_frag(inpkt); 
				    mb_join(xvc->reassemble,temp);  
				}

				/*
				    Free control block containing the
				    header
				*/
				m_freem(inpkt);                       
				inpkt=NULL;

				/*
				    Update length. If end of NDSU or Dbit set
				    or length greater than specified for assembled
				    NSDUs then mark NSDU to be sent to user
				*/
				xvc->nsdulen += len;

				if( (unsigned int)xvc->nsdulen >= (unsigned int)xvc->nslim
				    || Mbitin == 0 || Dbitin != 0)
				{
				    inpkt = xvc->reassemble;
				    xvc->reassemble = NULL;
				    xvc->nsdulen = 0;
				    sendin = 1;
				}
				else
				{
				    /*
					NSDU not complete so ack and reserve
					the buffer if no confs outstanding
					from the application.
				    */
				    if ( xvc->Dreq_rcvd_cnt == 0)
				    {
#ifdef MOD8ONLY
					xvc->nrtogo=((xvc->nrtogo+1)&7);
#else
					xvc->nrtogo=((xvc->nrtogo+1)&xvc->xmodulo);
				    }
#endif
				    sendin=0;
				}
			    }
			    else if( Mbitin!=0 )
			    {
				/*
				    Start of a NSDU. If Dbit set or length
				    greater than specified for assembled NSDUs
				    then mark to be sent to user.
				*/
				if( (unsigned int)len >=
				    (unsigned int)xvc->nslim || Dbitin != 0 )
				    sendin = 1;
				else
				{
				    /*
					Set up for reassembly, ack and reserve
					the buffer if no confs outstanding
					from the application
				    */
				    xvc->reassemble = inpkt;
				    inpkt = NULL;
				    xvc->nsdulen = len;

				    if ( xvc->Dreq_rcvd_cnt == 0 )
				    {

#ifdef MOD8ONLY
					xvc->nrtogo = ((xvc->nrtogo+1)&7);
#else
					xvc->nrtogo = ((xvc->nrtogo+1)&xvc->xmodulo);
#endif
				    }
				    sendin = 0;
				}
			    }
			    else
			    {
				/*
				    Simply mark to be sent to the user
				*/
				sendin=1;
			    }

			    if( sendin != 0 )
			    {
				/*
				    Check Dbit setting with control field.
				*/
				if ( Dbitin && (xvc->Dctl&DT_IN_ZERO) )
				    Dbitin = 0;

				if ( Dbitin && xvc->loc_Dsupport == RC_CONF_APP )
				{
				    /*
					If conf from application required then
					update count. If no confs were
					outstanding then mark where in the
					window we can acknowledge to, otherwise
					we have to save the sequence number
					for later.
					
				    */
				    if ( ++xvc->Dreq_rcvd_cnt == 1 )
					xvc->hold_nr = ns;
				    else
					xvc->Dreq_rcvd_seq[ns] = 1;
				}

				/*
				    If no confs from application
				    oustanding then we can acknowledge
				    this packet.
				*/
				if ( !xvc->Dreq_rcvd_cnt )
				    xvc->hold_nr = xvc->psexpected;

			    	/*
				    Data to go so set fields as required
				*/
				((struct xdataf FAR *)inpkt->b_rptr)->setQbit =
				    ( Qbitin != 0 ? 1 : 0 );
				((struct xdataf FAR *)inpkt->b_rptr)->More    =
				    ( Mbitin != 0 ? 1 : 0 );
				((struct xdataf FAR *)inpkt->b_rptr)->setDbit =
				    ( Dbitin != 0 ? 1 : 0 );
				if( xvc->xstate != DataTransfer &&
				    xvc->xstate != DXEbusy )
				{
				    /*
					PVC not ready states so save the data
					the user is ready.
				    */
				    sqrl_PVC_data(xvc,inpkt,0);
				}
				else
				if( (xvc->remcredit += DT_touser(xvc,inpkt,len)) > 0 )
				{
				    /*
					Data passed to the next queue OK. So
					mark if any acknowledgements can be
					sent.
				    */
				    while( xvc->remcredit > 0 &&
					   xvc->nrtogo != xvc->hold_nr )
				    {
#ifdef MOD8ONLY
					xvc->nrtogo = ((xvc->nrtogo + 1)&7);
#else
					xvc->nrtogo = ((xvc->nrtogo + 1)&xvc->xmodulo);
#endif
					xvc->remcredit--;
				    }
				}
				inpkt = NULL;
			    }

			    /*
				Carried acknowledgements may have opened
				window and allow tx data.
			    */
			    if( xvc->xstate == DataTransfer )
			    {
			    	/*
				    Expedited data takes priority. If
				    none pending then simply send any
				    queued data, else schedule the event
				    if required.
				*/
				if( (xvc->xusrevent&(xINTout|xINCout)) == 0 )
				    Tx_Qdata(xvc);
				else
				{
				    if( (int)xvc->acksoutstanding < (int)xvc->lwsize &&
				       (xvc->x25txqsize != 0 || xvc->datatogo != NULL ))
				    {
					xvc->xusrevent |= xDTout;
				    }
				}
				
				/*
				    If we had previously flow controled the user
				    and the carried acks have relieved it then
				    inform the user.
				*/
				if( packd && xvc->Udatawaiting &&
				   ((unsigned int)(xvc->acksoutstanding + 
					xvc->x25txqsize)
				   	< (unsigned int)xvc->lwsize) )
				{
 				    xvc->Udatawaiting=0;
				    FL_touser(xvc);
				}
			    }

			    /*
				Ack this frame now if ack can go and the window
				is closed. Note this implies no data was sent.
				Otherwise delay the ack for ackdelay time.
			    */
			    if( xvc->nrtogo != xvc->nrsent )
			    {
				if( xvc->ftoack == xvc->rwsize  )
				    maybe_ack(xvc);      /* Which may restart T24 */
				else
				    start_ackdelay(xvc); /* T24 left ticking    */
			    }
			}
			else
			{
			    /*
				M-bit packet short
			    */
			    diag=38;
			    goto daterr;
			}
		    }
		    else
		    {
			/*
			    Invalid p(s)
			*/
			diag=1;
			goto daterr;
		    }
		}
		else
		{
		    /*
			Invalid p(r)
		    */
		    diag=2;                   
		    goto daterr;
		}
	    }
	    else
	    {
		/*
		    Inconsistent Qbit setting
		*/
		diag=83;
		goto daterr;
	    }
	}
	else
	{
	    /*
		No support for Dbit or packet too long
	    */
	    if( Dbitin )
		diag = 166;
	    else
		diag = 39;          
	    goto daterr;
	}
    }
    else
    {
    	/*
	    No data in this state
	*/
	state_matrix(xvc,X_DATA); 
    }

discard_inpkt:

    if ( inpkt != NULL )
    	m_freem(inpkt);
	
    return;

daterr:       /* Error in data packet */

    if( diag == 38 || diag == 39 )
	P4_FMT_error((struct x25vcformat FAR *)xvc,diag);
    else
	ERR_rstU(xvc,(RREMOTE_PROCEDURE_ERROR<<8)|diag);

    goto discard_inpkt;

}

