#ident "$Id: x25nsuif.c,v 1.6 1996/12/13 18:58:03 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
 *
 *  X25NSUIF.C
 *
 *
 *******************************************************************/

/*
 *	x25nsuif.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25nsuif.c
 *	@(#)x25nsuif.c	1.54
 *
 *	Last delta created	16:10:47 6/4/92
 *	This file extracted	18:33:01 11/19/92
 *
 */

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

     X25 NETWORK SERVICE INTERFACE ROUTINES

   Routines to provide the ISO network service interface although some
   'wormholes' are provided in order to access some aspects useful in
   diagnostics.

   All routines conform to the rule that the returned value from the
   procedure call take the form:

               -1  : catastrophic error
                0  : successful primitive request
               >0  : in 'details', returns 'wormhole diagnostics',
                     in requests - try later (e.g. pressure)

   COMMENT:
   Each of the access routines checks references etc.  This could be
   firmed up by enforcing a gate/task port pair where the ns_ref
   and its user can be matched in a much more secure fashion.
   ---------------------------------------------------------------------- */
#include "x25_inc.h"

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

#ifndef NULL
#define NULL 0
#endif

extern	struct x25vcformat FAR *x25allocvc(int *);

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

/******************************************************************/
int N_putdata(nsref, outbuff)
/******************************************************************/

/*
    Data request - the buff carries M/Q flags. Returns 0 unless
    pressure exerted
*/

int nsref;
mblk_t *outbuff;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting data, the following checks are made:
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect);
	 - vc transmit queue size is compatible (no state)
	 - Dbit setting on data is compatible.
    */

    if( outbuff )
    {
	if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
	{
	    xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	    if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	    {
		if( (unsigned int)(xvc->x25txqsize+xvc->acksoutstanding)<(unsigned int)xvc->lwsize )
		{
		    /*
			Dbit ok if not set, or set and rem support, or no rem
			support but non cons and control info doesnt reset.
			See map_dbit_control().
		    */
		    if( ((struct xdataf FAR *)outbuff->b_rptr)->setDbit == 0
		                                  || !(xvc->Dctl&DT_OUT_RST) )
		    {
			/*
			    Mod Dbit setting according to dbit control
			    if required.
			*/
			if( xvc->Dctl&DT_OUT_ZERO )
			    ((struct xdataf FAR *)outbuff->b_rptr)->setDbit = 0;

			/* Enqueue outbuff on xvc->x25txqhead */
			if( xvc->x25txqtail != NULL )
			{
			    xvc->x25txqtail->b_next=outbuff;
			}
			else
			{
			    xvc->x25txqhead=outbuff;
			}

			xvc->x25txqtail=outbuff;
			outbuff->b_next=NULL;
			xvc->x25txqsize++;

			xvc->xusrevent|=xDTout;
		    }
		    else
		    {
			/*
			    No Dbit support so reset connection. This is
			    easiest requested by doing a buffer request with
			    the desired state marked as pending.
			*/
			xvc->xusrevent|=xBUFreq ; xvc->bureason=Usr_Req;
			xvc->xstate = WRCUpending;

			xvc->nsucause=0;	 /* Set up diags */
			xvc->nsudiag = xvc->rstdiag = 166;
			xvc->rstcause = RLOCAL_PROCEDURE_ERROR;
			
			freemsg(outbuff);	 /* Free offending buffer */
		    }
		    x25vccall( xvc );
		    return(0);                   /* OK */
		}
		else
		{
		    xvc->Udatawaiting=1;         /* Signal exerting pressure */
		    return(1);
		}
	    }
	    else
		system_error(NSUbadref);        /* Supplied ref not X25 etc. */
	}
	else
	    system_error(NSUrefrange);          /* Ref out of range */
    }
    else
	system_error(NSUdtnull);                /* NULL pointer to data */

    return(-1);                                 /* Catastrophy!! */
}


/************************************************************************/
int N_datack(nsref)
/************************************************************************/

/*
    Data acknowledgement.
*/

int nsref;

{

    register struct x25vcformat FAR *xvc;

    /*
	Before accepting ack, the following checks are made :
	    nsref within the range;
	    vc pointed to by nsref is tagged as X25;
	    vc and user are attached (no disconnect);
    */

    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    if (xvc->Dreq_rcvd_cnt)
	    {
		/*
		    Mark that this Dbit req has been acked by the user
		*/
		xvc->Dreq_rcvd_seq[xvc->hold_nr] = 0;
		
		/*
		    Decrement outstanding confs from application.
		    Update upper bound on N(R) to next outstanding
		    conf or next P(S) expected depending on whether
		    outstanding confs still exist.
		*/
		if ( --xvc->Dreq_rcvd_cnt )
		    xvc->hold_nr = next_Dreq(xvc);
		else
		    xvc->hold_nr = xvc->psexpected;

		/*
		    Convey acknowledgements to the remote end
		    immediately.
		*/
		xvc->ack_immediate = 1;
		xvc->xusrevent|=xAKout;
		x25vccall(xvc);
	    }

	    return(0);                         /* OK */
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    return(-1);                                /* Catastrophy !! */
}


/************************************************************************/
int N_putexpdata(nsref, outbuff)
/************************************************************************/

/*
    Expedited data request returns 0 unless pressure exerted
*/

int nsref;
mblk_t *outbuff;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting expedited data, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect);
	 - vc holds no expedited currently in network (else usrerror).
    */
    if( outbuff )
    {
	if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
	{
	    xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	    if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	    {
		if( xvc->xEDbuff==NULL && (xvc->xflag&INTOUTSTANDING)==0 )
		{
		    xvc->xEDbuff=outbuff;
		    xvc->xusrevent|=xINTout;
		    x25vccall( xvc );
		    return(0);                     /* OK */
		}
		else
		{
		    xusrerror(xvc,224);
		    return(1);         /* Signal exerting pressure */
		}
	    }
	    else
		system_error(NSUbadref);           /* Supplied ref not X25 etc. */
	}
	else
	    system_error(NSUrefrange);             /* Ref out of range */
    }
    else
	system_error(NSUednull);                   /* NULL pointer to data */
	
    return(-1);                                    /* Catastrophy !! */
}


/************************************************************************/
int N_expack(nsref)
/************************************************************************/

/*
    Expedited data acknowledgement.
*/

int nsref;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting expedited ack, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect);
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];
	
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xvc->xusrevent|=xINCout;
	    x25vccall( xvc );
	    return(0);                         /* OK */
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    return(-1);                                /* Catastrophy !! */
}


/************************************************************************/
int N_rstreq(nsref, cause, diag)
/************************************************************************/

/*
    Reset request
*/

int nsref;
unsigned char cause;
unsigned char diag;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting a reset request, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect).
	
	After requesting reset, the NS user must wait for an explicit
	confirmation from the NS provider.
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xvc->xusrevent|=xRSTout;
	    xvc->nsucause= (!xvc->CONS_call && cause != 0) ? (cause | 0x80) : 0;
	    xvc->nsudiag=diag;
	    x25vccall( xvc );
	    return(0);
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    return(-1);                                /* Catastrophy!! */
}


/************************************************************************/
int N_rstrsp(nsref)
/************************************************************************/

/*
   Reset response
*/

int nsref;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting a reset response, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect).
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xvc->xusrevent|=xRSCout;
	    x25vccall( xvc ); /* 'coz may call in IND? */
	    return(0);
	}
	else
	    system_error(NSUbadref);       /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    return(-1);                                /* Catastrophy!! */
}

/************************************************************************/
int N_disreq(nsref, cause, diag, qos, responding, deflected, userdata) 
/************************************************************************/

/*
     N_disreq  : disconnect request
     Originator is NS user 
*/

int nsref;
unsigned char cause;
unsigned char diag;
struct qosformat *qos;
struct xaddrf FAR *responding;
struct xaddrf FAR *deflected;
mblk_t  *userdata;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting a disconnect request, the following checks are made :

	  - nsref within the range;
	  - vc pointed to by nsref is tagged as X25;
	  - vc and user are attached (no disconnect).

	After requesting disconnect, the NS user must not make any further
	requests on this reference.
    */

    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xvc->xusrevent|=xDRout;
	    xvc->nsucause= (!xvc->CONS_call && cause != 0) ? (cause | 0x80) : 0;
	    xvc->nsudiag=diag;
#ifdef INTERNAL_DISC
	    xvc->NSUattached=0;
#endif
	    /*
		If pending disconnect state, then this disc will take
		precedence.  Delete previous Udiscdata
	    */
	    if( xvc->Udiscdata )
		m_freem(xvc->Udiscdata);
	    xvc->Udiscdata=userdata;
	    
	    if( responding != NULL )
		bcopy( (bufp_t)responding,
		       (bufp_t)&xvc->rcalled, sizeof(struct xaddrf) );

	    if (xvc->xdomain == WAN88 || xvc->xdomain == LAN88)
	    {
		/*
		    Clear request with delection
	            Only from 88 onwards
		*/
		if( qos && qos->xtras.call_deflect )
		{
		    /*
			Set reason for deflection to at least C0.
			Deflecting DTE should set bits 1 to 6.
		    */
		    xvc->call_redirect = (qos->xtras.call_deflect | 0xC0);

		    if( deflected != NULL )
			bcopy( (bufp_t)deflected,
			       (bufp_t)&xvc->orig_add, 
			        sizeof(struct xaddrf));

		    rq_facilities(X_CLR,xvc,(struct qosformat FAR *)qos); 
		}
	    }

#ifndef INTERNAL_DISC
	    x25vccall(xvc);
#else
	    /*
		No 'x25vccall( xvc )' since the user may
		disconnect in response to an incoming call or
		internally (eg timer) generated reset, in which
		case the scheduler may be confused (CHECK!).
	    */
#endif
	    return(0);                  /* OK                        */
	}
	else
	    system_error(NSUbadref);    /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);      /* Ref out of range          */

    return(-1);                         /* Catastropy!!              */
}


/************************************************************************/
int N_RIdetails(nsref,originator,reason)
/************************************************************************/

/*
    Used to read details after receiving a reset indication.
*/

int nsref;
unsigned char FAR *originator, FAR *reason;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before returning the diagnostics, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect).
	 
	No check is made on the state of the vc.  If an error is detected
	the function returns -1, otherwise the returned value is
	reset cause << 8 + reset diagnostic.
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];
	
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    /*
		Map the reset cause and diagnostic to ISO network
		service reasons DP 8878
	    */
	    *originator=NS_PROVIDER;                 /* Until otherwise */

	    if( xvc->rstcause != 0 && (xvc->CONS_call || !(xvc->rstcause&0x80)) )
	    {
		if( xvc->rstcause==RNETWORK_CONGESTION )
		    *reason=NS_RCONGESTION;
		    
		else if( xvc->rstcause==RREMOTE_PROCEDURE_ERROR  ||
		         xvc->rstcause==RLOCAL_PROCEDURE_ERROR   ||
			 xvc->rstcause==RINCOMPATIBLE_DESTINATION )
		{
		    *reason=NS_RUNSPECIFIED;
		}
		else
		{
		    *originator=(*reason)='\0'; /* Undefined */
		}
	    }
	    else
	    {
		if( xvc->rstdiag==234 )              /* reset -congestion */
		    *reason=NS_RCONGESTION;
		    
		else if( xvc->rstdiag==250 )         /* reset -resync     */
		{
		    *originator=NS_USER;
		    *reason=NU_RESYNC;
		}
		
		else
		    *reason=NS_RUNSPECIFIED;
	    }
	    return( (xvc->rstcause<<8) + xvc->rstdiag);
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    /*
	ERROR, return -1
    */
    *originator=(*reason)=0;
    return(-1);
}


/************************************************************************/
int N_DIdetails(nsref,originator,reason,respondingaddress,qos)
/************************************************************************/

/*
    Uused to read details after reception of a disconnect indication.
*/

int nsref;
unsigned char FAR *originator, FAR *reason;
struct xaddrf FAR *respondingaddress;
struct qosformat   FAR *qos;

{
    register struct x25vcformat FAR *xvc;
    struct xaddrf FAR *rptr;
    unsigned char   CRreject;
    unsigned char   discause;

    /*
	Before returning the diagnostics, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
 
	Note that the vc and user are not assumed to be attached
	since the disconnect will have detached them.

	No check is made on the state of the vc.  If an error is detected
	the function returns -1, otherwise the returned value is
	disconnect cause << 8 + disconnect diagnostic.
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG )
	{
	    /*
		Map the disconnect cause and diagnostic to ISO
		network service reasons DP 8878
	    */
	    CRreject=((xvc->xflag&caareq)==0);      /* To map to OSI */
	    *originator=NS_PROVIDER;                 /* Until otherwise */
	    discause = xvc->discause & 127;

	    if( discause != 0 && (xvc->CONS_call || !(xvc->discause&0x80)) )
	    {
		if( CRreject )
		{
		    if( discause==NOT_OBTAINABLE ||
		        discause==SHIP_ABSENT ) /* Yo! Ho! Ho! */
		    {
			*reason=NS_NSAPUNKNOWN;
		    }
		    else if( discause==ACCESS_BARRED ||
		         discause==FS_ACCEPTANCE_NOT_SUBSCRIBED ||
		         discause==INCOMPATIBLE_DESTINATION     ||
		         discause==INVALID_FACILITY_REQUEST     ||
		         discause==OUT_OF_ORDER                 ||
		         discause==REMOTE_PROCEDURE_ERROR       ||
		         discause==LOCAL_PROCEDURE_ERROR        ||
		         discause==REVERSE_CHARGING_NOT_SUBSCRIBED ||
		         discause==RPOA_OUT_OF_ORDER               ||
		         xvc->disdiag ==121   ||   /* Unknown called DNIC */
		         xvc->disdiag ==122 )      /* Maintenance action  */
		    {
			*reason=NS_PUNSPECIFIED;
		    }
		    else if( discause==NETWORK_CONGESTION      ||
		             discause==NUMBER_BUSY             ||
		        (xvc->disdiag<=120 && xvc->disdiag>=112 ))
		    {
			*reason=NS_TUNSPECIFIED;
		    }
		    else
			*originator=(*reason)='\0';         /* Undefined? */
		}
		else
		{
		    if( discause==OUT_OF_ORDER       ||
		        discause==REMOTE_PROCEDURE_ERROR ||
			discause==LOCAL_PROCEDURE_ERROR  ||
			discause==RPOA_OUT_OF_ORDER     ||
			xvc->disdiag ==122 )
		    {
			*reason=NS_DPERMANENT;
		    }
		    else if( discause==NETWORK_CONGESTION ||
		             xvc->disdiag ==113                ||
			     xvc->disdiag ==115 )
		    {
			*reason=NS_DTRANSIENT;
		    }
		    else
			*originator=(*reason)='\0';   /* Undefined? */
		}
	    }
	    else                           /* DTE originated */
	    {
		if( xvc->disdiag==241  ||         /* disconnection - normal   */
		    xvc->disdiag==242  ||         /* disconnection - abnormal */
		    (xvc->disdiag>=244  &&         /* rejection - HL generated */
		    xvc->disdiag<=248) )
		{
		    *originator=NS_USER;
		    *reason=xvc->disdiag;              /* One to One mapping */
		}
		else if( (xvc->disdiag>=225 &&         /* disc - transient */
		          xvc->disdiag<=232 )  ||      /* rej - NSAP       */
			  xvc->disdiag==235 )
		{
		    *reason=xvc->disdiag;              /* One to One mapping */
		}
		else
		{
		    if( CRreject )
		    {
			if( xvc->disdiag==164  ||      /* Fast select subs. */
			    xvc->disdiag==121  ||
			    xvc->disdiag==122  )
			{
			    *reason=NS_PUNSPECIFIED;
			}
			else if( xvc->disdiag>=112 &&
			         xvc->disdiag<=120 )
			{
			    *reason=NS_TUNSPECIFIED;
			}
			else
			{
			    *reason=NS_TUNSPECIFIED;  /* For all other codes */
			}
		    }
		    else
		    {
			if( xvc->disdiag==122 )
			    *reason=NS_DPERMANENT;
			else if( xvc->disdiag==113 ||
			         xvc->disdiag==115 ||
				 xvc->disdiag==162 ||    /* DTE not operational */
				 xvc->disdiag==163 )     /* DTE resource constr */
			{
			    *reason=NS_DTRANSIENT;
			}
			else
			    *originator=(*reason)='\0';  /* Undefined? */
		    }
		}
	    }
	    
	    /*
		OSI reasons etc. matched, now supply the address
		 ?????? what to do when local clear etc. ?????
	    */
	    if( xvc->rcalled.nsap_len!=0 ||
		xvc->rcalled.DTE_MAC.lsap_len != 0 )  /* There is a resp. */
	    {
		rptr = (&xvc->rcalled);
	    }
	    else
	    {
		rptr = (&xvc->called);
	    }
	    bcopy( (bufp_t)rptr, (bufp_t)respondingaddress,
	                               sizeof(struct xaddrf));

	    /*
		Load any indication of quality of service parameters
	    */
	    sh_disc_facilities(1,xvc,qos);   

	    return( (xvc->discause<<8) + xvc->disdiag);
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    /*
	ERROR, return -1
    */
    *originator=(*reason)=0;

    return(-1);
}


/************************************************************************/
static void OK_tcneg(tl,tr,xvc)
/************************************************************************/

/*
    Verify negotiation of tclass - used for CAA only. l = local, r = remote
*/

unsigned char tl, tr;
register struct x25vcformat FAR *xvc;

{
    register struct wlcfg *xx;

    xx=xvc->xbase;

    /*
	Because of TELENET case, ignore if not indicated
    */
    if( (xvc->ISOflag&TCLASS)!=0 && (xvc->xflag&thruforced)==0 )
    {
	if( xx->psdn_local.thclass_neg_to_def )
	{
	    if( ((tl>=xx->locdefthclass && tl<=xvc->lthru) ||
	         (tl<=xx->locdefthclass && tl>=xvc->lthru) )  )
	    {
		xvc->lthru = tl;
	    }
	    
	    if( ((tr>=xx->remdefthclass && tr<=xvc->rthru) ||
	         (tr<=xx->remdefthclass && tr>=xvc->rthru) ) )
	    {
		xvc->rthru = tr;
	    }
	}
	else				/* Must be <= indicated values */
	{
	    if( tl<=xvc->lthru ) xvc->lthru = tl;
	
	    if( tr<=xvc->rthru ) xvc->rthru = tr;
	}
    }
}

/************************************************************************/
static void rq_facilities(pkt_type,xvc,qos)
/************************************************************************/

/*
    rq_facilities : selects facilities in conreq and conrsp
*/

int pkt_type;
register struct x25vcformat FAR *xvc;
register struct qosformat   FAR *qos;

{

    struct wlcfg *xx;

    int is_call_req = (pkt_type == X_CALL);
    int is_con_resp = (pkt_type == X_CAA);
    int is_clr_req  = (pkt_type == X_CLR);

    xx = xvc->xbase;

    /*
	If not a call request then reset called and calling facility lengths,
	priority length, and protection lengths incase they were set in the
	incoming call. Will be set if any present.
    */
    if ( !is_call_req )
    {
	xvc->x_fac_len = xvc->cg_fac_len = xvc->cd_fac_len = 0;
	xvc->prty_len  = 0;
	xvc->prot_len  = xvc->lowprot_len = xvc->protection_type = 0;
    }

    if( qos )
    {
	/*
	    Throughput Class negotiation
	*/
	if( qos->reqtclass )
	{
	    if( is_call_req )
	    {
		xvc->lthru=(unsigned char) qos->locthroughput;
		xvc->rthru=(unsigned char) qos->remthroughput;
		xvc->ISOflag|=TCLASS;

		/*
		   Postpone setting of MTCLASS until network is known -
		   see fix_def_facilities
		*/
	    }
	    else		/* CAA - what is negotiated? */
	    {
		OK_tcneg((unsigned char) qos->locthroughput,
			     (unsigned char) qos->remthroughput,xvc);
	    }
	}

	if( ( is_call_req || is_clr_req ) && qos->reqminthruput )
	{
	    xvc->lminthru=(unsigned char) qos->locminthru;
	    xvc->rminthru=(unsigned char) qos->remminthru;
	    xvc->CCITTflag|=MTCLASS;
	}
	
	/*
	    Transit Delay negotiation
	*/
	if( ( is_call_req || is_clr_req ) && qos->reqtransitdelay )
	{
	    xvc->CCITTflag|=ENDTOEND;
	    xvc->etoetarget    =(unsigned char) qos->transitdelay;
	    xvc->CCITTflag|=ENDTARGET;

	    /*
		Check here implies MAX not without TARGET
	    */
	    if( qos->reqmaxtransitdelay )
	    {
		xvc->etoemaxaccept =(unsigned char) qos->acceptable;
		xvc->CCITTflag|=ENDMAX;
	    }

	    /*
		For a clear request, we will reset the values of
		the etoecumulative and ltrans to the values that
		an initial call request would set them to.
		Note that the check here implies that call deflection
		facility has been requested.
	     */
	    if ( is_clr_req )
	    {
		struct wlcfg *xx;
		
		xx=xvc->xbase;
		xvc->etoecumulative = xx->localdelay + xx->accessdelay;
		xvc->ltrans         = xvc->etoetarget - xvc->etoecumulative;
	    }
	}

	/*
	    Expedited data negotiation.
	*/
	if( qos->reqexpedited )
	{
	    if( is_call_req || is_clr_req )
		xvc->CCITTflag|=(EXPDATANEG|EXPSELECTED);
	    else
		if( (xvc->CCITTflag&EXPDATANEG) != 0 )
	             xvc->CCITTflag|=EXPINUSE;    
	}
	else
	{
	    if( xvc->CCITTflag&EXPSELECTED )      /* Must be CAA & resp */
		xvc->CCITTflag&= ~(EXPDATANEG|EXPINUSE);
	}

	/*
	    Receipt Confirmation negotiation
	*/
	if( qos->reqackservice && !is_clr_req )
	{
	    xvc->loc_Dsupport=(unsigned char) qos->reqackservice;
	}

	/*
	    Priority negotiation.
	    For X.25(88), values cannot be greater than 15 for CONS calls.
	*/
	if( qos->reqpriority )
	{
	    xvc->prtyvalue = (unsigned char) qos->reqpriority;
	    xvc->xflag |= Prty_flag;

	    /*
		Preset priority fields to undefined.
	    */
	    (void) memset((char FAR *)xvc->prty,255,MAX_PRTY_LEN);

	    xvc->prty_len = 1;
	    xvc->prty[0]  = (xvc->CONS_call && qos->prtydata > 15)
			  ? 15 : qos->prtydata;


	    if( qos->reqprtygain )
	    {
		xvc->prty_len = 2;
		xvc->prty[1]  = (xvc->CONS_call && qos->prtygain > 15)
			      ? 15 : qos->prtygain;
	    }

	    if( qos->reqprtykeep )
	    {
		xvc->prty_len = 3;
		xvc->prty[2]  = (xvc->CONS_call && qos->prtykeep > 15)
			      ? 15 : qos->prtykeep;
	    }

	    if( is_call_req || is_clr_req )
	    {
		if( qos->reqlowprtydata )
		{
		    xvc->prty_len = 4;
		    xvc->prty[3]  = (xvc->CONS_call && qos->lowprtydata > 15)
				  ? 15 : qos->lowprtydata;
		}

		if( qos->reqlowprtygain )
		{
		    xvc->prty_len = 5;
		    xvc->prty[4]  = (xvc->CONS_call && qos->lowprtygain > 15)
				  ? 15 : qos->lowprtygain;
	        }

		if( qos->reqlowprtykeep )
		{
		    xvc->prty_len = 6;
		    xvc->prty[5]  = (xvc->CONS_call && qos->lowprtykeep > 15)
				  ? 15 : qos->lowprtykeep;
		}
	    }

	}

	/*
	    Protection negotiation
	*/
	if( qos->protection_type )
	{
	    /*
		Set protection lengths and types.
		If protection type is invalid, default to 'globally unique'.
	    */ 
	    xvc->protection_type = (qos->protection_type > PRT_GLB)
					? PRT_GLB : qos->protection_type;

	    xvc->prot_len = (qos->prot_len > MAX_PROT)
					? MAX_PROT : qos->prot_len;	
	    bcopy((bufp_t)qos->protection,
		  (bufp_t)xvc->protection, xvc->prot_len);

	    /* Only Call/Clr Req. packets have lowest acceptable protection */
	    if ( is_call_req || is_clr_req )
	    {
	    	xvc->lowprot_len = (qos->lowprot_len > MAX_PROT)
					? MAX_PROT : qos->lowprot_len;	

	    	bcopy((bufp_t)qos->lowprotection,
		      (bufp_t)xvc->lowprotection, xvc->lowprot_len);
	    }
	    else
	    	xvc->lowprot_len = 0;
	 }

	/* Extras */

	if( (qos->xtras.pwoptions&4) != 0 )
	{
	    xvc->nslim = (ushort) qos->xtras.nsdulimit;
	    xvc->xflag|= LIM_ASSERTED;
	}

	/*
	    Network User Identifier
	*/
        if( qos->xtras.nui_len != 0 )
	{
	    xvc->ISOflag |= NUIREQ;
	    xvc->nui_len = (qos->xtras.nui_len > MAX_NUI_LEN) ? MAX_NUI_LEN :
							   qos->xtras.nui_len;
	    bcopy((bufp_t)qos->xtras.nui_field,
		  (bufp_t)xvc->nui_field,xvc->nui_len);
	}

	/*
	    Request Charging Information
	*/
	if( qos->xtras.reqcharging != 0 )
	    xvc->ISOflag |= CHGREQ;


	/*
	    Programmable Facility Field
	    User should set lengths to zero if not required.
	*/
	xvc->x_fac_len  = (unsigned char) qos->xtras.x_fac_len;
	xvc->cg_fac_len = (unsigned char) qos->xtras.cg_fac_len;
	xvc->cd_fac_len = (unsigned char) qos->xtras.cd_fac_len;
	bcopy((bufp_t)qos->xtras.fac_field,
	      (bufp_t)xvc->fac_field, MAX_FAC_LEN);

	if( is_call_req )
	{
	    if( qos->xtras.fastselreq )
		xvc->ISOflag|=FSELECT;

	    if( qos->xtras.restrictresponse )
	        xvc->ISOflag|=FSRRESP;

	    if( qos->xtras.reversecharges )
		xvc->ISOflag|=REVCHARGE;

	    /*
		We do not yet know WAN/LAN so postpone default P/W setting
		Note that the user specifying zero here is still OK
	    */
	    xvc->lpktp = (unsigned char) qos->xtras.locpacket;
	    xvc->rpktp = (unsigned char) qos->xtras.rempacket;
	    xvc->lwsize= (unsigned char) qos->xtras.locwsize;
	    xvc->rwsize= (unsigned char) qos->xtras.remwsize;

	    /*
		Closed User Group
	    */
	    if( qos->xtras.cug_type != 0 )       /* Ignore unknown types */
	    {
		if( qos->xtras.cug_type == CUG )
		    xvc->ISOflag |= CUGREQ;
		else if( qos->xtras.cug_type == BCUG )
		    xvc->ISOflag |= BCUGREQ;
		    
		bcopy((bufp_t)qos->xtras.cug_field,
		      (bufp_t)xvc->cug_index,MAX_CUG_LEN);
	    }

	    /*
		Recognised Private Operating Agency
	    */
	    if( qos->xtras.rpoa_len != 0 )
	    {
		xvc->ISOflag |= RPOAREQ;

		if( (int)qos->xtras.rpoa_len > MAX_RPOA_LEN << 1 )
 		    qos->xtras.rpoa_len = MAX_RPOA_LEN << 1;

		/*
		    Work out number of rpoas supplied rounded up if necessary.
		    This specifies the number of bytes which will be copied
		    into the facilities, regardless if rpoa_len is less.
		*/
		xvc->rpoa_len = qos->xtras.rpoa_len / 4;
		if ( qos->xtras.rpoa_len % 4 ) xvc->rpoa_len++;

		/*
		    Expect length to be a multiple of 4 and hence even.
	    	    Any odd semioctets are not copied and will be replaced
		    by zero.
		*/
		bzero((bufp_t)xvc->rpoa_field,MAX_RPOA_LEN);
		bcopy((bufp_t)qos->xtras.rpoa_field,
		      (bufp_t)xvc->rpoa_field,qos->xtras.rpoa_len >> 1);
	    }
	}
	else /* CLR or CAA */
	{
	    if( is_con_resp )			/* Connection response */
	    {
		if( (xvc->xflag&PK_OK) != 0 )  /* Otherwise ignore ns user */
		{
		    OK_pneg((unsigned char)qos->xtras.locpacket,
				    (unsigned char)qos->xtras.rempacket,xvc);
		}
	    	if( (xvc->xflag&WS_OK) != 0 )  /* Otherwise ignore ns user */
		{
		    OK_wneg((unsigned char)qos->xtras.locwsize,
				    (unsigned char)qos->xtras.remwsize,xvc);
		}
	    }
	}
    }
    else                                 /* Qos passed NULL */
    {
	if( is_call_req )
	{
	    xvc->loc_Dsupport=0;	 /* default to no Dbit support */
#ifdef	EXPDATA_DEFAULT
	    if( xvc->X29_inuse == 0 && xcfg.ASKEXPDATA )
		xvc->CCITTflag|=(EXPDATANEG|EXPSELECTED);
#endif
	}
	else                             /* Clear request / Connect response */
	{
	    /*
		If expedited data was requested in the incoming call
		then the user implicitly accepts.
	    */
	    if( (xvc->CCITTflag&EXPDATANEG) != 0 )
		xvc->CCITTflag |= EXPINUSE;  

	    /*
		If receipt confirmation was requested in the incoming
		call then the user implicitly accepts.
	    */
	    if ( xvc->rem_Dsupport && !is_clr_req )
	    	xvc->loc_Dsupport = xvc->rem_Dsupport;  

	    /*
		For clear request packets, unless the user explicitly
		specifies transit delay parameters, they are cleared
	    */
	    if ( is_clr_req )
        	xvc->CCITTflag&=~ENDTOEND;
	}
	/*
	    We do not yet know WAN/LAN so postpone default P/W setting
	*/
    }

    /*
	If CONS procedures are not in use on this call, and no attempt
	has been made to negotiate usage of expedited data, then by
	default expedited data is in use.
    */
    if( !xvc->CONS_call && !(xvc->CCITTflag&EXPSELECTED) )
	xvc->CCITTflag |= EXPINUSE;
}


/************************************************************************/
static void sh_facilities(is_con_ind,xvc,qos)
/************************************************************************/

/*
    Plant facilities in records
*/

int is_con_ind;
register struct x25vcformat FAR *xvc;
register struct qosformat   FAR *qos;

{

    struct wlcfg *xx;

    xx =xvc->xbase;

    if( qos ) /* Safety */
    {
	bzero((bufp_t)qos, sizeof(struct qosformat));

	if( (xvc->ISOflag&TCLASS)!=0 && (xvc->xflag&thruforced) == 0 )
	{
	    qos->locthroughput=xvc->lthru;
	    qos->remthroughput=xvc->rthru;
	    qos->reqtclass=1;
	}
	else
	    qos->reqtclass=0;

	/* Max Acceptabletransit delay  */
	if( is_con_ind && (xvc->CCITTflag&ENDMAX)!=0 ) /* Not CAA */
	{
	    qos->acceptable=xvc->etoemaxaccept;
	    qos->reqmaxtransitdelay=1;
	}
	else
	    qos->reqmaxtransitdelay=0;

	/* End to End transit delay - comment IWU needs up to 3? */
	if( (xvc->CCITTflag&ENDTOEND)!=0 )
	{
	    qos->transitdelay=xvc->etoecumulative;
	    qos->reqtransitdelay=1;
	}
	else
	    qos->reqtransitdelay=0;

	if( is_con_ind && (xvc->CCITTflag&MTCLASS)!=0 ) /* Not CAA */
	{
	    qos->locminthru=xvc->lminthru;
	    qos->remminthru=xvc->rminthru;
	    qos->reqminthruput=1;
	}
	else
	    qos->reqminthruput=0;

	/*
	    Value shown is independent of the 'selected' flag. Also to cater
	    for the X29 case, con_ind includes the EXPINUSE flag
	*/
	if( is_con_ind )
	{
	    if( (xvc->CCITTflag&(EXPDATANEG|EXPINUSE))!=0 )
	    {
		qos->reqexpedited = 1;
	    }
	    else
		qos->reqexpedited = 0;
	}
	else
	    qos->reqexpedited=( (xvc->CCITTflag&EXPINUSE  )!=0 ? 1 : 0);

	qos->reqackservice=xvc->rem_Dsupport;

	if( !is_con_ind && xvc->rem_Dsupport && !xvc->loc_Dsupport )
	{
	    /*
   	        Call confirmation with clash in D-bit support. Check
		configuration for indication to user.
	    */
	    if( xx->psdn_local.dbit_control&CC_IN_ZERO )
	    	qos->reqackservice = 0;

	}

	/* Call Priority */
	switch (xvc->prty_len)
	{

	/*
	    For all cases, if running over CONS,
	    then maximum priority is 15
	*/
	case 6: 
		qos->reqlowprtykeep = 1;
		qos->lowprtykeep = (( xvc->CONS_call ) &&
		                    ( xvc->prty[5] > 15 )) ? 15 : xvc->prty[5];
		/* Fall Through */
	case 5: 
		qos->reqlowprtygain = 1;
		qos->lowprtygain = (( xvc->CONS_call ) && 
				    ( xvc->prty[4] > 15 )) ? 15 : xvc->prty[4];
		/* Fall Through */
	case 4: 
		qos->reqlowprtydata = 1;
		qos->lowprtydata = (( xvc->CONS_call ) &&
				    ( xvc->prty[3] > 15 )) ? 15 : xvc->prty[3];
		/* Fall Through */
	case 3: 
		qos->reqprtykeep = 1;
		qos->prtykeep = (( xvc->CONS_call ) &&
				 ( xvc->prty[2] > 15 )) ? 15 : xvc->prty[2];
		/* Fall Through */
	case 2: 
		qos->reqprtygain = 1;
		qos->prtygain = (( xvc->CONS_call ) && 
				 ( xvc->prty[1] > 15 )) ? 15 : xvc->prty[1];
		/* Fall Through */
	case 1: 
		qos->reqpriority = 1;
		qos->prtydata = (( xvc->CONS_call ) &&
				 ( xvc->prty[0] > 15 )) ? 15 : xvc->prty[0];
		break;

	default: /* No action required - Will not occur due to earlier check */
		break;
	}
		
	/* Call Protection */
	if (xvc->protection_type)
	{
	    qos->protection_type = xvc->protection_type;
	    qos->prot_len = xvc->prot_len;
	    qos->lowprot_len = xvc->lowprot_len;

	    /* Protection lengths checked previously */
	    bcopy((bufp_t)xvc->protection,
		  (bufp_t)qos->protection, xvc->prot_len);
	    bcopy((bufp_t)xvc->lowprotection,
		  (bufp_t)qos->lowprotection, xvc->lowprot_len);
	}

	/* Extra facility indications */

	if( is_con_ind )
	{
	    qos->xtras.fastselreq      =( (xvc->ISOflag&FSELECT)  !=0 ? 1 : 0);
	    qos->xtras.restrictresponse=( (xvc->ISOflag&FSRRESP)  !=0 ? 1 : 0);
	    qos->xtras.reversecharges  =( (xvc->ISOflag&REVCHARGE)!=0 ? 1 : 0);

	    if( (xvc->ISOflag&PSIZENEG )!=0 &&
	        (xvc->lpktp != 7 || xvc->rpktp != 7) &&
		(xvc->xflag&(Prty_slug|thruforced))==0 )
	    {
		xvc->xflag            |= PK_OK; /* User can negotiate in CAA */
		qos->xtras.pwoptions   = 1;
	    }
	    else
		qos->xtras.pwoptions   = 0;
	    
	    if( (xvc->ISOflag&WSIZENEG )!=0 &&
	        (xvc->lwsize != 2 || xvc->rwsize != 2)  && (xvc->xflag&thruforced)==0)
	    {
		xvc->xflag            |= WS_OK; /* User can negotiate in CAA */
		qos->xtras.pwoptions  |= 2;
	    }
	
	    if( (xvc->ISOflag&(CUGREQ|BCUGREQ)) != 0 )
	    {
		qos->xtras.cug_type = (xvc->ISOflag & CUGREQ) ? CUG : BCUG;
		qos->xtras.cug_field[0] = xvc->cug_index[0];
		qos->xtras.cug_field[1] = xvc->cug_index[1];
	    }

	    qos->xtras.nsdulimit       =   xvc->nslim;

	    /*
		Pass up any called or calling facilities in the programmable
		facility field. Lengths set to zero if none present.
	    */
	    qos->xtras.x_fac_len  = 0;
	    qos->xtras.cg_fac_len = xvc->cg_fac_len;
	    qos->xtras.cd_fac_len = xvc->cd_fac_len;

	    /*
		If x_fac_len set it implies that the facilities arrived in a
		different order to that defined for the programmable facility
		field. Thus we have to reverse it.
	    */
	    if ( xvc->x_fac_len )
	    {
		bcopy( (bufp_t)(xvc->fac_field + xvc->cd_fac_len),
		       (bufp_t)qos->xtras.fac_field,
		        xvc->cg_fac_len );

		bcopy( (bufp_t)xvc->fac_field,
		       (bufp_t)(qos->xtras.fac_field + xvc->cg_fac_len),
		        xvc->cd_fac_len );
	    }
	    else
	    {	
		bcopy( (bufp_t)xvc->fac_field,
		       (bufp_t)qos->xtras.fac_field,
		        MAX_FAC_LEN );
	    }
	    
	    /*
		Address redirections
	    */
	    if ( qos->xtras.call_redirect = xvc->call_redirect )
	    {
		qos->xtras.called.lsap_len = xvc->orig_add.DTE_MAC.lsap_len;
		bcopy(&xvc->orig_add.DTE_MAC.lsap_add[0],
		      &qos->xtras.called.lsap_add[0],
		      LSAPMAXSIZE);
	    }
	}

	qos->xtras.locpacket=xvc->lpktp;
	qos->xtras.rempacket=xvc->rpktp;
	qos->xtras.locwsize =xvc->lwsize;
	qos->xtras.remwsize =xvc->rwsize;

	/*
	    Address modifications
	*/
	qos->xtras.called_add_mod = xvc->called_add_mod;
    }
}


/************************************************************************/
void sh_disc_facilities(is_disc_ind,xvc,qos)
/************************************************************************/

/*
    Plant facilities in records
*/

int is_disc_ind;
register struct x25vcformat FAR *xvc;
register struct qosformat   FAR *qos;

{
    if( qos ) /* Safety */
    {
	bzero((bufp_t)qos, sizeof(struct qosformat));
	
	/*
	    Address modifications
	*/
	if ( is_disc_ind )
	{
	    qos->xtras.called_add_mod = xvc->called_add_mod;
	}

	/*  Call charging information  */
	if (xvc->ch_cd_len)
	{
	    qos->xtras.chg_cd_len = xvc->ch_cd_len;
	    bcopy(xvc->ch_cd_field, qos->xtras.chg_cd_field, xvc->ch_cd_len);
	}

	if (xvc->ch_sc_len)
	{
	    qos->xtras.chg_sc_len = xvc->ch_sc_len;
	    bcopy(xvc->ch_sc_field, qos->xtras.chg_sc_field, xvc->ch_sc_len);
	}

	if (xvc->ch_mu_len)
	{
	    qos->xtras.chg_mu_len = xvc->ch_mu_len;
	    bcopy(xvc->ch_mu_field, qos->xtras.chg_mu_field, xvc->ch_mu_len);
	}
    }
}


/************************************************************************/
static void request_NRS(x)
/************************************************************************/

/*
    Dummy routine for when this functionality required
*/

register struct x25vcformat FAR *x;
{
    x->xusrevent|=xNRSreply;
    x->xflag    |=NRSidentified;
}


/************************************************************************/
int N_conreq(userref,nsref, cons, calledaddr, callingaddr, qos, userdata)
/************************************************************************/

/*
    Called to set up a connection.  Note that two references are used
    - user and a NS level. If the request is accepted a ZERO value is
    returned, else a diagnostic reason (with connotation of N_P originato
    r i.e. a disconnect).
*/

int                  userref, *nsref;
unsigned char	     cons;
struct xaddrf        FAR *calledaddr, FAR *callingaddr;
struct   qosformat   FAR *qos;
mblk_t	             *userdata;

{

    /*
	Extras is a window into the X25 packet level by which packet size
	and window sizes and other nonstandard (OSI) facilities may be
	requested by the user.  It is not part of the OSI
	service, and should not be relied on (e.g. stricty speaking
	the quality of service should determine windows and so on - a project
	for evaluation).  Should the user want to employ only default values
	then it is sufficient to pass a NULL pointer to this record. It is
	is also important to note that this implementation does not (yet?)
	police any of the values passed.
    */

    register struct x25vcformat FAR *xvc;
    int ref;

    /* Obtain a database structure for this connect request */

    if( (xvc=x25allocvc(&ref)) )
    {
	/* Obtained a record, tag it and plant facilities */
	xvc->xtag=X25TAG;
	xvc->nsref=ref;                    /* For referring to self  */
	xvc->userref=userref;
	xvc->calldata=userdata;            /* USER nulls his pointer */
	xvc->NSUattached=1;                /* A user now attached    */
	xvc->est_time=curtime();           /* SNMP: Establish time      */
	xvc->direction=X25_DRCTN_OUT;      /* SNMP: Establish direction */

	bcopy( (bufp_t)calledaddr ,
	       (bufp_t)&xvc->called, sizeof(struct xaddrf));
	bcopy( (bufp_t)callingaddr,
	       (bufp_t)&xvc->caller, sizeof(struct xaddrf));

	xvc->CONS_call=cons;	       /* Whether to use CONS procedures */

	/* Load up quality of service information - if not all default */

	rq_facilities(X_CALL,xvc,(struct qosformat FAR *)qos);

	xvc->lpkt =(1<<xvc->lpktp);
	xvc->rpkt =(1<<xvc->rpktp);

	/*
	    Information required is now loaded, ask the NRS to validate
	    the NSAP address and to provide the necessary LSAP
	*/
	xvc->xstate=AskingNRS;
	
	/*
	    This is a hook for management of addresses but is
	    a dummy call at this time to set the event and OK bits
	*/
	request_NRS(xvc);

	/*
	    Set the users reference and return 0
	*/
	*nsref=ref;
	return(0);                             /* Return OK result */
    }
    return(NS_TUNSPECIFIED);                   /* No resources */
}


/************************************************************************/
int N_conrsp(userref,nsref, cons, respondingaddr, qos, userdata)
/************************************************************************/

/*
    Called to negotiate parameters immediately after a connect indication.
    Note that the two references are required - nsref to point to the circuit
    and userref to identify the user.
*/

int                  userref, nsref;
unsigned char 	     cons;
struct xaddrf        FAR *respondingaddr;
struct   qosformat   FAR *qos;
mblk_t               *userdata;

{
    /*
	Extras is a window into the X25 packet level by which packet size
	and window sizes and other nonstandard (OSI) facilities may be
	negotiated by the user.  It is not part of the OSI
	service, and should not be relied on (e.g. stricty speaking
	the quality of service should determine windows and so on - a project
	for evaluation).  Should the user want to employ the values indicated
	in the connect, it is sufficient to pass a null pointer to this
	record.  NB no values are policed.
    */

    register struct x25vcformat FAR *xvc;

    /*
	Point to the database structure for this connect response
    */
    if( nsref>=FIRSTVC && nsref<=isovcmax )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG )
	{
	    xvc->userref=userref;
	    xvc->calldata=userdata;            /* USER nulls his pointer */

	    if( respondingaddr != NULL )
		bcopy( (bufp_t)respondingaddr,
		        (bufp_t)&xvc->rcalled, sizeof(struct xaddrf));
			
	    xvc->CONS_call=cons;	       /* Whether to use CONS procedures */

	    /*
		Load up quality of service information - if negotiated
	    */
	    rq_facilities(X_CAA,xvc,(struct qosformat FAR *)qos);  

	    xvc->lpkt =(1<<xvc->lpktp);
	    xvc->rpkt =(1<<xvc->rpktp);

	    /*
		Call is being accepted.  Set the appropriate event, mark
		that a connect response was requested and return
	    */
	    xvc->xflag    |=caareq;     /* For diagnostic mapping */
	    xvc->xusrevent|=xCAAout;

	    if (SNMP_trace_q)
		SNMP_callparm_event(xvc, xvc->calldata) ;

	    return(0);
	}
	else
	    system_error(NSUbadref);
    }
    else
	system_error(NSUrefrange);

    return(-1);                         /* Catastrophy */
}


/************************************************************************/
int N_CIdetails(nsref,calledaddr,callingaddr,qos)
/************************************************************************/

/*
    Called to acquire details on incoming connect indication.
*/

int                nsref;
unsigned char      FAR *calledaddr, FAR *callingaddr;
struct qosformat   FAR *qos;

{
    /*
	As in all the connect interface routines, the extras structure
	is a window into the packet level.
    */

    register struct x25vcformat FAR *xvc;

    /*
	Point at the structure for this cannect
    */
    if( nsref>=FIRSTVC && nsref<=isovcmax )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
/*	    xvc->called.sn_id = xvc->xu_ident;
	    xvc->called.aflags= ( (xvc->CCITTflag&EXTDADDR) !=0 ? 1 : 0); */

	    /*
		Provide calling DTE and subnetwork in in the xaddrf structure
	    */
	    bcopy( (bufp_t)&xvc->called,
	           (bufp_t)calledaddr , sizeof(struct xaddrf));
		   
	    bcopy( (bufp_t)&xvc->caller,
	           (bufp_t)callingaddr, sizeof(struct xaddrf));

	    /*
		Load up the quality of service parameters
	    */
	    sh_facilities(1,xvc,(struct qosformat FAR *)qos);    /* 1 -> connect indication */

	    return(0);
	}
	else
	    system_error(NSUbadref);
    }
    else
	system_error(NSUrefrange);

    return(-1);
}


/************************************************************************/
int N_CCdetails(nsref,respondingaddr,qos)
/************************************************************************/

/*
    Called to acquire details on incoming connect response.
*/

int                nsref;
unsigned char      FAR *respondingaddr;
struct qosformat   FAR *qos;

{
    /*
	As in all the connect interface routines, the extras structure
	is a window into the packet level.
    */
    register struct x25vcformat FAR *xvc;

    /*
	Point at the structure for this cannect
    */
    if( nsref>=FIRSTVC && nsref<=isovcmax )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];

	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    if( xvc->rcalled.nsap_len !=0 || xvc->rcalled.DTE_MAC.lsap_len != 0 )
		bcopy( (bufp_t)&xvc->rcalled,
		       (bufp_t)respondingaddr, sizeof(struct xaddrf));
	    else
		bcopy( (bufp_t)&xvc->called,
		       (bufp_t)respondingaddr, sizeof(struct xaddrf) );

	    /*
		Load up the quality of service parameters
	    */
	    sh_facilities(0,xvc,(struct qosformat FAR *)qos);    /* 0 -> connect response */

	    /*
		Call is being accepted so map Dbit control info if appropriate
		for Data packets.
	    */
	    map_dbit_control(xvc);

	    return(0);
	}
	else
	    system_error(NSUbadref);
    }
    else
	system_error(NSUrefrange);

    return(-1);                          /* Catastrophy!! */
}


/************************************************************************/
void N_enableinput(nsref, credit)
/************************************************************************/

/*
    Called to permit more input from the network.
*/

int nsref, credit;

{
    register struct x25vcformat FAR *xvc;

    /*
	Before accepting credit, the following checks are made :
	 - nsref within the range;
	 - vc pointed to by nsref is tagged as X25;
	 - vc and user are attached (no disconnect);

	No state check is made here, but the code will only enact
	if the state is in one of the data transfer cases.
    */
    if( (nsref>=FIRSTVC) && (nsref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[nsref];
	
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xvc->remcredit+=credit;        /* Increment current count */
	    xvc->xusrevent|=xAKout;
	    x25vccall( xvc );
	    LL_enable(xvc->lsaplink);      /* Unblock flow control     */
	    return;						   /* OK */
	}
	else
	    system_error(NSUbadref);           /* Supplied ref not X25 etc. */
    }
    else
	system_error(NSUrefrange);             /* Ref out of range */

    return ;                                /* Credit failed */
}

