#ident "$Id: x25userev.c,v 1.5 1997/09/23 16:38:29 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
 *
 *  X25USEREV.C
 *
 *
 *******************************************************************/

/*
 *	x25userev.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25userev.c
 *	@(#)x25userev.c	1.61
 *
 *	Last delta created	15:11:55 11/16/92
 *	This file extracted	18:33:08 11/19/92
 *
 */

#include "x25_inc.h"

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

#ifndef NULL
#define NULL 0
#endif

extern mblk_t *allocb();

/******************************************************************/
struct lsformat FAR *match_lsap(cfg,a,dom)
/******************************************************************/

/*
    Hunts the lsap records for a one with the same config record.  If
    LAN, checks address. Returns a pointer to it (or NULL)
*/

struct wlcfg *cfg;
struct xaddrf FAR *a;
int dom;

{
    int i, j;
    struct lsformat FAR *lsp;

    j = (dom == LAN84 || dom == LAN88);

    for(i=FIRSTLSAP; i<=isolsapmax; i++)
    {
	lsp = iso_lsap[i];

	if( lsp->ltag == PLPTAG )
        {
	    if( lsp->lbase == cfg )		/* Similar record */
	    {
		if( j )			/* If LAN address must match */
		{
		    if( ( lsp->ldomain == LAN84 || lsp->ldomain == LAN88 ) &&
		          a->DTE_MAC.lsap_len == lsp->lladdr.lsap_len &&
			  x25cmp((bufp_t)&a->DTE_MAC.lsap_add[0],
			  (bufp_t)&lsp->lladdr.lsap_add[0], 7 ) == 0 )
		    {
			return( lsp ); /* 7 byte length plumbed */
		    }
		}
		else
	        {
		    if( lsp->ldomain != LAN84 && lsp->ldomain != LAN88 )
			return(lsp);	/* Same domain & same cfg (sn_id) */
	        }
	    }
	}
    }
    
    return((struct lsformat FAR *)NULL);
}


/******************************************************************/
static int xfirst(z)
/******************************************************************/

/*
    Returns the first set event value.
*/

register unsigned short z;

{
    register int i;

    for( i=1; i<16; i++, z>>=1)
	if( (z&1) != 0) return(i);

    return(0);
}


/******************************************************************/
void xusrerror(xv,d)
/******************************************************************/

/*
    Called when user makes a faux pas. Monitor the occurrence and set
    the internal disconnect event
*/

register struct x25vcformat FAR *xv;
register unsigned char d;

{
    L3monitor(L3usererror, 0);
    
    xv->nsucause = 0;
    xv->discause = LOCAL_PROCEDURE_ERROR;
    xv->nsudiag  = xv->disdiag  = d;

    xv->xusrevent|= xUSRerr;
}


/************************************************************************/
static void puta(ptr,plen,acd,acg)
/************************************************************************/

/*
    Called to insert DTE address into packets
*/

struct lsapformat *acd, *acg;
unsigned char *ptr;
int *plen;

{
    unsigned char total;
    unsigned char puttop;
    unsigned char gotbyt, bytgot, putbyt;
    unsigned char i, nibble;
    unsigned char FAR *arrptr;
    struct lsapformat NilAddr ;

    NilAddr.lsap_len = 0 ;
    if (!acd) acd = &NilAddr ;
    if (!acg) acg = &NilAddr ;

    /*
	If Abit set then one octet for each length else one nibble
	for each length.
    */
    if( ptr[0]&0X80 )
    {
	ptr[(*plen)++] = ( (unsigned char) acd->lsap_len );
	ptr[(*plen)++] = ( (unsigned char) acg->lsap_len );
    }
    else	
	ptr[(*plen)++] = ( (unsigned char) ((acg->lsap_len<<4)+acd->lsap_len) );

    total  = ( acd->lsap_len + acg->lsap_len ); /* Total number of nibbles */
    puttop = 1;                       /* Put top nibble               */
    gotbyt = 0;                       /* No byte yet obtained for nib */
    arrptr = (&acd->lsap_add[0]);      /* Start with called address    */

    for( i=0; i < total; i++)
    {
	if( i == acd->lsap_len )        /* Just past called address     */
	{
	    gotbyt = 0;                /* Get a new byte               */
	    arrptr = (&acg->lsap_add[0]); /* Over to calling address now  */
	}

	/*
	    If we have no byte, then get one
	*/
	if( gotbyt == 0 ) 
	{
	    bytgot = (*arrptr++);
	    nibble = ((bytgot>>4)&15);
	    gotbyt = 1;                /* Got byte and top nibble    */
	}
	else
	{
	    nibble = ((bytgot   )&15);
	    gotbyt = 0;                /* Discard this byte now      */
	}

	/*
	    Now put the nibble into the buffer
	*/
	if( puttop )
	{
	    putbyt = ( nibble<<4 );
	    puttop = 0;
	}
	else
	{
	    putbyt |= nibble;
	    puttop  = 1;
	}

	/*
	    Enter the byte into the buffer
	*/
	if( puttop != '\0' || i == (total - 1) )
	    ptr[(*plen)++] = (putbyt);
    }
}


/******************************************************************/
static int load_NSAP( ptr, plen, faccode, adptr, fm_pres )
/******************************************************************/

/*
    Used to put address into buffer primed by pointer and index.
    Returns byte length loaded.
*/

register unsigned char *ptr;
int  *plen;
unsigned char faccode;
struct xaddrf FAR *adptr;
int *fm_pres;

{
    int i, adl, extflag, fl;

    /*
	Initialise nothing loaded
    */
    adl = fl = 0; 

    if( adptr != NULL && adptr->nsap_len != '\0')
    {
	/*
	    Something to load. Insert facility marker if not already
	    present.
	*/
	if ( !(*fm_pres) )
        {
	    ptr[(*plen)++] = (0);
	    ptr[(*plen)++] = (0X0F);
	    fl=2;
	    *fm_pres = 1;
	}
	
	extflag = ((adptr->aflags&EXT_ADDR) ? 1 : 0 ); /* Extended address? */
	ptr[(*plen)++] = (faccode);	               /* Facility          */
	
	adl = (unsigned char)(adptr->nsap_len+1)>>(unsigned char)1;     /* Byte len address  */
	ptr[(*plen)++] = ( (unsigned char) (1+adl) );  /* Octet length      */
	ptr[(*plen)  ] = (unsigned char) ( adptr->nsap_len );          /* Entire OSI        */

	if( extflag )
	    ptr[(*plen)] |= 0X80;                      /* Entire non-OSI    */

	(*plen)++;
	for( i=0; i<adl; i++ )
	    ptr[(*plen)++] = ( adptr->NSAP[i] );
	    
	adl += 3;                                      /* Length loaded     */
    }

    return(adl + fl);
}

/************************************************************************/
mblk_t *make_c_packet(xvc,ufn)
/************************************************************************/

/*
    make_c_packet : creates a call/call confirm/clr packet based
                    on the facilities in the data base.  If
                    successful, return pointer to buffer chain.
*/

register struct x25vcformat FAR *xvc;
unsigned char ufn;

{
    struct wlcfg *xx;
    unsigned char *fladd;
    struct lsapformat *ptrcalled, *ptrcalling;
    unsigned char al, ah, len, pkt[256];
    int is_call_req, is_clr_req, pktlen;
    mblk_t *buf1, *buf2, *rbuf;
    struct xaddrf FAR *b;
    int cgfm_pres, ccittfm_pres, flen, i;
    int is_1988;
    int Dbit, Abit;
    unsigned char checkval1=0;

    xvc=xvc;
    xx =xvc->xbase;
    
    is_call_req = ( ufn == CALL_PTI );
    is_clr_req  = ( ufn == CLR_PTI );
    is_1988     = (xvc->xdomain == WAN88 || xvc->xdomain == LAN88);

    /*
	Initialise Facility Length - inserted after facilities
    */
    len=0;

    /*
	After building packet, this code acquires a buffer for
	the header and packet and, at the end for calls out, a
	response buffer for the call accept - if ever.

	Insert the lgn and lcn plus packet type (repaired for CALL)
	and, if clear, the diagnostic/cause bytes
    */

    Dbit = xvc->loc_Dsupport;

    if( Dbit && !is_call_req && !xvc->rem_Dsupport ) 
    {
	/*
	    Call accept with clash in D bit support. Check control
	    information for mod to Dbit setting. NB non CONS call.
	*/
	if( xx->psdn_local.dbit_control&CC_OUT_ZERO )
	    Dbit = 0;
    }
	
    pkt[0] = ((unsigned char)(xvc->lgngfi | (is_clr_req ? 0X00 :
                                            (Dbit ? 0X40 : 0X00))));
    /*
	Set Abit in GFI (bit 8) if required
    */
    if( xx->SUB_MODES&SUB_TOA_NPI_FMT )
	pkt[0] |= 0X80;

    pkt[1] = (xvc->lcn);
    pkt[2] = (ufn);
    pktlen=3;			/* Index into pkt initialised */

    if( is_clr_req )
    {
	pkt[pktlen++] = (xvc->discause);
	pkt[pktlen++] = (xvc->disdiag);

	if( xvc->Udiscdata == NULL && xvc->call_redirect == 0 )
	    goto trim;       /* And avoid the following code */
    }

    /*
	1. Address length and field:
	(JNT spec - zero for LAN)
        (zero calling for Bilateral CUGS )
    */
    al='\0';
    ptrcalling = (&xvc->caller.DTE_MAC);

    if( is_call_req && (xvc->xdomain != LAN84 && xvc->xdomain != LAN88) )
    {
	ptrcalled =(&xvc->called.DTE_MAC);
#ifdef DEBUG
printf("src_addr_control = %x \n",xx->psdn_local.src_addr_control);
#endif
	switch( xx->psdn_local.src_addr_control )
	{
	    default:
	    break;

	    /* Force calling DTE address to zero */
	    case 1:
#ifdef DEBUG
printf("case 1\n");
#endif
		ptrcalling = (struct lsapformat *) 0 ;

	    break;

	    /* Use preferred if non requested */	
	    case 2:
#ifdef DEBUG
printf("case 2\n");
#endif
		if( xvc->caller.DTE_MAC.lsap_len == 0 )
		    ptrcalling =(&xx->local_address);
		    
	    break;

	    /* Force to preferred value */
	    case 3:
#ifdef DEBUG
printf("case 3\n");
#endif
		ptrcalling =(&xx->local_address);

	    break;
	}

	/*
	    Set called DTE address length to zero for Bilatral
	    Closed User Groups.
	*/
        if( xvc->ISOflag&BCUGREQ ) ptrcalled = (struct lsapformat *) 0 ;
    }
    else
    {
	/*
	    All other cases involve zero SNPA load
	*/
	ptrcalled = ptrcalling = (struct lsapformat *) 0 ;
    }

    puta(&pkt[0], &pktlen, ptrcalled, ptrcalling);
    fladd = (&pkt[pktlen]);   /* Obtain address where fac length goes */
    pkt[pktlen++] = ('\0');   /* and plant dummy value pro tem        */

    /*
	Load the facility list into the buffer.  Note that 256 bytes
	should be enough to cater for all facilities if set - the maximum
	facility length is 109 and is now checked at the end.

	ISO facilities must come first. 

	Fast Select - always requested to support N-service unless
	running the connection as a WAN80
    */
    if( is_clr_req )
    {
	/*
	    Call deflected facility only valid 1988 onwards
	*/
	if( xvc->call_redirect && is_1988 )
	{
	    int no_octets = ((int)(xvc->orig_add.DTE_MAC.lsap_len + 1) >> 1);

	    pkt[pktlen++] = (0xD1);

	    /* Length in octets of DTE to deflect to */
	    pkt[pktlen++] = no_octets + 2;

	    /* Reason for deflection */		
	    pkt[pktlen++] = (0xC0);

	    /* Length in semi-octets of DTE to deflect to */
	    pkt[pktlen++] = (unsigned char) xvc->orig_add.DTE_MAC.lsap_len;
	    len += 4;

	    bcopy((bufp_t)xvc->orig_add.DTE_MAC.lsap_add,
		  (bufp_t)&pkt[pktlen], no_octets);

	    pktlen += no_octets;
	    len += (unsigned char) no_octets;

	    goto CCITTfac;        /* Add CCITT facs */
	}
	else                      /* Proceed no further in this code  */
	    goto trim;
    }

    if( is_call_req )
    {
	/*
	    Fixup the fast select situation wrt. CONS and X.29 over X.25
	    as well as the DATAPAC priority bit
	*/
	if( mb_bulen(xvc->calldata)>16 || xvc->CONS_call )
	    xvc->ISOflag |= FSELECT;

	/*
	    Fixup reverse charging situation wrt. local charging prevention
	*/
	if ( xx->SUB_MODES&SUB_LOC_CHG_PREV )
	    xvc->ISOflag |= REVCHARGE;

	if( (xvc->ISOflag&(FSELECT|REVCHARGE|FSRRESP)) != 0 ||
	     ((xvc->xflag&Prty_flag)!=0 &&
	       xx->psdn_local.prty_encode_control==1) )
	{
	    pkt[pktlen++] = (0X01);
	    al = 0;
	    if( (xvc->ISOflag&FSELECT)   != 0 )
		al |= 0X80;
	    if( (xvc->ISOflag&REVCHARGE) != 0 ) /* Reverse charging? */
		al |= 1;
	    if( (xvc->ISOflag&FSRRESP)   != 0 )
		al |= 0XC0;                     /* Or in RRESP bit   */
	    if( ((xvc->xflag&Prty_flag)!=0 && xx->psdn_local.prty_encode_control==1) )
		al |= 0x02;			    /* DATAPAC priority ctl */
	    pkt[pktlen++] = (al);
	    len+=2;
	}
    }

    /*
	If FSRRESP is requested, then skip the following facilities
    */
    if( is_call_req && (xvc->ISOflag&FSRRESP)!=0 )
	goto fac_skipped;

    /*
	Throughput Class Negotiation
    */
    if( (xvc->ISOflag&TCLASS) != 0 )
    {
	pkt[pktlen++] = (0X02);
	if( is_call_req )
	{
	    ah = xvc->rthru;
	    al = xvc->lthru;
	}
	else
	{
	    ah = xvc->lthru;
	    al = xvc->rthru;
	}

	/* Modify if high and low nibble hacks required */
	if( xx->psdn_local.thclass_type == 1 )
	    ah = 0;
	else if( xx->psdn_local.thclass_type == 2 )
	    al = 0;

	pkt[pktlen++] = ( (unsigned char) ((ah<<4) + al) );
	len+=2;
    }

    if (xvc->NUI_override)
	checkval1=xvc->nuibase.SUB_MODES&SUB_EXTENDED;
    else
	checkval1=xvc->xbase->SUB_MODES&SUB_EXTENDED;
	
    /*
	Window Size Negotiation
    */
    if( (is_call_req && (checkval1) != 0 ) ||
	(xvc->ISOflag&WSIZENEG) != 0 ) /* select/indicate */
    {
	pkt[pktlen++] = (0X43);
	if( is_call_req )
	{
	    pkt[pktlen++] = (xvc->rwsize);
	    pkt[pktlen++] = (xvc->lwsize);
	}
	else
	{
	    pkt[pktlen++] = (xvc->lwsize);
	    pkt[pktlen++] = (xvc->rwsize);
	}
	len+=3;
    }

    /*
	Packet Size Negotiation
    */
    if( (is_call_req && (checkval1) != 0 ) ||
        (xvc->ISOflag&PSIZENEG) != 0 ) /* select/indicate */
    {
	pkt[pktlen++] = (0X42);
	if( is_call_req )
	{
	    pkt[pktlen++] = (xvc->rpktp);
	    pkt[pktlen++] = (xvc->lpktp);
	}
	else
	{
	    pkt[pktlen++] = (xvc->lpktp);
	    pkt[pktlen++] = (xvc->rpktp);
	}
	len+=3;
    }

    /*
	Transit Delay Indication - ignore request in 1980 networks
    */
    if( is_call_req && (xvc->ISOflag&TRDELAY) != 0 && xvc->xdomain!=WAN80)
    {
	pkt[pktlen++] = (0X49);
	pkt[pktlen++] = ((unsigned char) (xvc->ltrans>>8)    );
	pkt[pktlen++] = ((unsigned char) (xvc->ltrans)   &255);
	len+=3;
    }


    if (xvc->NUI_override)
	checkval1= (unsigned char) xvc->nuibase.CUG_CONTROL;
    else
	checkval1= (unsigned char) xx->CUG_CONTROL;
	
    /*
	Closed User Group Indication
    */
    if( is_call_req && (xvc->ISOflag&CUGREQ) != 0 )
    {
	/*
	    If subscribe to a preferential CUG or 1980 then cant use
	    CUG/OA facility on this interface. If 1980 then also cant
	    use extended format
	*/ 
	if( checkval1&SUB_CUG || checkval1&SUB_PREF || xvc->xdomain == WAN80 )
	{
	    if( checkval1&EXTENDED && xvc->xdomain != WAN80 )
		pkt[pktlen++] = (0x47);
	    else
		pkt[pktlen++] = (0x03);
	}
	else
	{
	    pkt[pktlen++] = (checkval1&EXTENDED) ? (0x48) : (0x09); /* CUG/OA */
	}

	/*
	    The CUG index is supplied as a right justified 4 digit number,
	    so copy in the required semi octets for the format and domain.
	    Extended format is contained in two bytes, Basic in one.
	*/
	if( checkval1&EXTENDED && xvc->xdomain != WAN80 ) 
   	{
	    pkt[pktlen++] = xvc->cug_index[0];
	    len++;
	}

	pkt[pktlen++] = xvc->cug_index[1];

	len += 2;
    }

    /*
	Bilateral Closed User Group Indication
    */
    if( is_call_req && (xvc->ISOflag&BCUGREQ) != 0 )
    {
	pkt[pktlen++] = (0x41);
	pkt[pktlen++] = xvc->cug_index[0];
	pkt[pktlen++] = xvc->cug_index[1];
	len += 3;
    }

    /*
	Network User Identification - not applicable to WAN80
    */    
    if( (xvc->ISOflag&NUIREQ) != 0 && xvc->xdomain != WAN80 )
    {
	pkt[pktlen++] = (0xC6);
	pkt[pktlen++] = xvc->nui_len;
	bcopy((bufp_t)xvc->nui_field,(bufp_t)&pkt[pktlen],xvc->nui_len);

	pktlen += xvc->nui_len;
	len    += xvc->nui_len + 2;
    }

    /*
	Recognised Private Operating Agency
    */
    if( is_call_req && (xvc->ISOflag&RPOAREQ) != 0 )
    {
	if( xvc->xdomain == WAN80 || xvc->rpoa_len == 1 )
	{
	    /*
	        WAN80 can only use BASIC format of encoding (2 bytes)
	    */
	    pkt[pktlen++] = (0x44);
	    pkt[pktlen++] = xvc->rpoa_field[0];
	    pkt[pktlen++] = xvc->rpoa_field[1];
	    len += 3;
	}
	else
	{
	    /*
	        Extended encoding 4, 6 or 8 bytes
	    */
	    pkt[pktlen++] = (0xC4);
	    pkt[pktlen++] = xvc->rpoa_len << 1;
	    bcopy((bufp_t)xvc->rpoa_field,(bufp_t)&pkt[pktlen],xvc->rpoa_len << 1);

	    pktlen += xvc->rpoa_len << 1;
	    len += (xvc->rpoa_len << 1) + 2;
	}
    }

    /*
	Charging Information Requesting Service - not applicable to WAN80
    */
    if ( (xvc->ISOflag&CHGREQ) != 0 && xvc->xdomain != WAN80 )
    {
	pkt[pktlen++] = (0x04);
	pkt[pktlen++] = (0x01);
	len += 2;
    }
    

    /*
	Supported ISO facilities complete
    */
fac_skipped:

    /*
	User Supplied X.25 Facilities
	Initialise user supplied fac length
    */
    flen = 0;     


    if( xvc->x_fac_len )
    {
	for( i=0 ; i < (int)xvc->x_fac_len && flen < MAX_FAC_LEN ; i++ )
	    pkt[pktlen++] = xvc->fac_field[flen++];
    }

    /*
	Calling Network Facilites
    	No calling facilities marker present yet.
    */
    cgfm_pres = 0;


    /*
	Examine priority for DATAPAC 1980
    */
    if( is_call_req &&
        ((xvc->xflag&Prty_flag)!=0 && xx->psdn_local.prty_encode_control==2) )
    {
	pkt[pktlen++] = 0x00;		/* Calling Net Facility Marker */
	pkt[pktlen++] = 0x00;
	pkt[pktlen++] = 0x01;		/* Facility */
	pkt[pktlen++] = 0x01;
	len += 4;
	cgfm_pres = 1;
    }

    /*
	User Supplied from the programmable facility field
    */
    if( xvc->cg_fac_len && flen < MAX_FAC_LEN )
    {
	if( !cgfm_pres )
	{
	    pkt[pktlen++] = 0x00;
	    pkt[pktlen++] = 0x00;
	    len += 2;
	}
    
	for( i=0 ; i < (int)xvc->cg_fac_len && flen < MAX_FAC_LEN ; i++ )
	    pkt[pktlen++] = xvc->fac_field[flen++];
    }

    /*
	Called Network Facilities 
	User supplied from the programmable facility field
    */
    if( xvc->cd_fac_len && flen < MAX_FAC_LEN )
    {
	pkt[pktlen++] = 0x00;
	pkt[pktlen++] = 0xFF;
	len += 2;
    
	for( i=0 ; i < (int)xvc->cd_fac_len && flen < MAX_FAC_LEN ; i++ )
	    pkt[pktlen++] = xvc->fac_field[flen++];
    }

    /*
	Add in the length of any user supplied facilities
	to the total length.
    */
    len += (unsigned char) flen;
    
CCITTfac:
    /*
	4. CCITT facilities
	In this case, it is known that the CCITT facilities will be
	required.  It is assumed that no length checks are required
	BEFORE the addresses are loaded into the buffer.
    */


    /*
	Facility Marker. Skip this in 1980 networks
    */
    if( xvc->xdomain == WAN80 ) goto loadlen;  /* For mixed cases */

    /*
	To facilitate 1980 calls on an 1984 network, the CCITT
	facilities marker is only inserted in the packet if any
	of the CCITT facilties are required.
    */
    ccittfm_pres = 0;

    /*
	Expedited Negotiation
    */
    if( (xvc->CCITTflag&EXPSELECTED) != 0 )
    {
	if ( !ccittfm_pres )
	{
	    pkt[pktlen++] = (0);
	    pkt[pktlen++] = (0X0F);     /* CCITT */
	    len+=2;
	    ccittfm_pres = 1;    	/* Marker now preset */
	}

	pkt[pktlen++] = (0X0B);
	pkt[pktlen++] = ( (xvc->CCITTflag&EXPDATANEG) != 0 ? 0X01 : 0X00 );
	len+=2;
    }

    /*
	Minimum Throughput Negotiation
    */
    if( ( is_call_req || is_clr_req ) && (xvc->CCITTflag&MTCLASS) != 0 )
    {
	if ( !ccittfm_pres )
	{
	    pkt[pktlen++] = (0);
	    pkt[pktlen++] = (0X0F); /* CCITT */
	    len+=2;
	    ccittfm_pres = 1;	    /* Marker now preset */
	}

	pkt[pktlen++] = (0X0A);
	pkt[pktlen++] = ((unsigned char) ((xvc->rminthru<<4) + xvc->lminthru));
	len+=2;
    }

    /*
	End to End Transit Delay
    */
    if( (xvc->CCITTflag&ENDTOEND) != 0 )
    {
	if ( !ccittfm_pres )
	{
	    pkt[pktlen++] = (0);
	    pkt[pktlen++] = (0X0F);     /* CCITT */
	    len+=2;
	    ccittfm_pres = 1;    	/* Marker now preset */
	}

	pkt[pktlen++] = (0XCA);
	al = 2;                          /* Always at least cumulative */

	if( is_call_req )
	{
	    if( (xvc->CCITTflag&ENDMAX) == 0 )
		al=4;
	    else
		al=6;
	}
	pkt[pktlen++] = (al);
	pkt[pktlen++] = ((unsigned char) (xvc->etoecumulative>>8));
	pkt[pktlen++] = ((unsigned char) (xvc->etoecumulative&255));
	len+=4;

	/* Now reset cumulative (for CAA selected) in case accept by default */
	if( is_call_req )
	    xvc->etoecumulative = xvc->etoetarget;

	if( al>2 )
        {
	    /* ENDTARGET (at least) is requested. */
	    pkt[pktlen++] = ((unsigned char) (xvc->etoetarget>>8));
	    pkt[pktlen++] = ((unsigned char) (xvc->etoetarget&255));
	    len+=2;

	    if( al>4 )
	    {
		pkt[pktlen++] = ((unsigned char) (xvc->etoemaxaccept>>8));
		pkt[pktlen++] = ((unsigned char) (xvc->etoemaxaccept&255));
		len+=2;
	    }
	}
    }

    /*
	Examine Priority for X.25(88)
    */
    if( is_1988 && (xvc->xflag&Prty_flag) != 0
                && xx->psdn_local.prty_encode_control == 0 )
    {
        /*
	    Check packet type. If clear, only valid when redirect set
	*/
        if( !is_clr_req || xvc->call_redirect )
        {
	    /*
		Check for any trailing 'unspecified' priorities
		which should not be present. Note that the length is
		assumed correct for packet type as set in 'rq_facilities'.
	    */
	    while (xvc->prty_len > 0 && xvc->prty[xvc->prty_len - 1] == 255)
		xvc->prty_len--;

	    if (xvc->prty_len != 0)
	    {
	        if ( !ccittfm_pres )
	        {
		    pkt[pktlen++] = (0);
		    pkt[pktlen++] = (0X0F);     /* CCITT */
		    len+=2;
		    ccittfm_pres = 1;    	/* Marker now preset */
	        }
    
	        pkt[pktlen++] = (0xD2);
	        pkt[pktlen++] = xvc->prty_len;
	        bcopy((bufp_t)xvc->prty, (bufp_t)&pkt[pktlen], xvc->prty_len);
	        pktlen += xvc->prty_len;
	        len += (xvc->prty_len + 2);
	    }
	}
    }

    /*
	Call Protection - 1988 Only
    */
    if( is_1988 && xvc->protection_type )
    {
	/* Check packet type. If clear, only valid when redirect set */
	if( !is_clr_req || xvc->call_redirect )
	{
	    if ( !ccittfm_pres )
	    {
		pkt[pktlen++] = (0);
		pkt[pktlen++] = (0X0F);     /* CCITT */
		len+=2;
		ccittfm_pres = 1;    	/* Marker now preset */
	    }

	    pkt[pktlen++] = (0xD3);

	    /*
		Note that lowprot_len assumed correct for packet type
		as set in 'rq_facilities
	    */
	    pkt[pktlen++] = xvc->prot_len + xvc->lowprot_len 
				      + 2 + (xvc->lowprot_len != 0);
	    pkt[pktlen++] = ((xvc->protection_type << 6) & 0xC0);
	    len += 3;

	    pkt[pktlen++] = xvc->prot_len;
	    bcopy((bufp_t)xvc->protection, (bufp_t)&pkt[pktlen], xvc->prot_len);
	    pktlen += xvc->prot_len;
	    len += (xvc->prot_len + 1);

	    if (xvc->lowprot_len != 0)
	    {
		pkt[pktlen++] = xvc->lowprot_len;
		bcopy((bufp_t)xvc->lowprotection, 
		      (bufp_t)&pkt[pktlen], xvc->lowprot_len);
		pktlen += xvc->lowprot_len;
		len += (xvc->lowprot_len + 1);
	    }

	}
    }

    /*
	Called Address Extension Facilities
    */
    if( is_clr_req )
    {
	b=(struct xaddrf FAR *)(&xvc->orig_add);
    }
    else
    {
        if( is_call_req || xvc->rcalled.nsap_len==0 ) /* Viz. CAA with no responder */
	{
	    b=(struct xaddrf FAR *)(&xvc->called);
	}
	else
	{
	    b=(struct xaddrf FAR *)(&xvc->rcalled);
	}
    }

    if( b )          /* Address to be loaded */
    {
	len += (unsigned char) load_NSAP( &pkt[0],&pktlen,(unsigned char)0XC9, b,&ccittfm_pres);
    }

    /*
	Calling Address Extension
    */
    if( is_call_req )
    {
	len += (unsigned char) load_NSAP( &pkt[0],&pktlen,(unsigned char)0XCB, &xvc->caller, &ccittfm_pres);
    }

    if( is_clr_req )
    {
	len += (unsigned char) load_NSAP( &pkt[0],&pktlen,(unsigned char)0XCB, &xvc->rcalled, &ccittfm_pres);
    }


    /*
	Supported CCITT facilities complete
	Insert facility length - recall address stored in fladd
    */
loadlen:                             
    *fladd = (len > 109) ? 109 : len;

trim:     /* Trim the buffers cudf etc */

    if( ufn==CAA_PTI )
    {
	if((xvc->ISOflag&FSELECT) == 0 )
	{
	    /*
		No fast select operating, delete call data in confirmation
	    */
	    m_freem(xvc->calldata);
	    xvc->calldata=NULL;

	    /*
		Next check the call for standard response etc.
	    */
	    if( pktlen == 5 )		/* No negotiation */
	    {
		if( xvc->xdomain == WAN80 )	/* Fast select is not operating */
		    pktlen = 3;		/* Send only the standard response */
	    }
	}
    }

    /*
	Obtain the buffers necessary for the packet constructed in pkt and,
	if the packet is a call, acquire the connect confirm buffer
    */
    if( (buf1=allocb(pktlen,BPRI_MED))!=NULL )
    {
	/*
	    Obtained the data buffer (already set to MT_DATA)
	    now get header
	*/
	if( (buf2=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL )
	{
	    buf2->b_cont=buf1;		/* Join second buffer */

	    /*
		If this is a call, get the cc buffer and hold in rbuf
	    */
	    if( is_call_req &&
	        (rbuf=allocb(sizeof(struct xccnff),BPRI_MED)) == NULL )
	    {
		m_freem(buf2);		/* Cancel the call */
		return(NULL);
	    }

	    /* Copy the packet to buffer */
	    bcopy((bufp_t)&pkt[0],(bufp_t)buf1->b_rptr,pktlen);

	    /* Modify length */
	    buf1->b_wptr = buf1->b_rptr + pktlen;

	    /* Append call/clear udf */
	    if( is_clr_req )
	    {
		buf1->b_cont=xvc->Udiscdata;
		xvc->Udiscdata=NULL;
	    }
	    else
	    {
		buf1->b_cont=xvc->calldata;
		xvc->calldata=NULL;

		/* Fix up response buffer */
		if( is_call_req )
		    xvc->calldata = rbuf;
	    }
	    
	    /*
		Adjust the user data length to 128 if too large
	    */
	    if( (pktlen = 128 - mb_bulen(buf1->b_cont)) < 0 )
		(void) adjmsg(buf1->b_cont,pktlen);


		if(buf1->b_cont)
		{
				printf("Makec = %x",*buf1->b_cont->b_rptr) ;
		}

	}
	else
	{
	    m_freem(buf1);   /* Buffer shortage */
	    return(NULL);
	}
    }
    else
	return(NULL);

    return(buf2);
}


/******************************************************************/
static int chk_addr(ad,dom,cd_cg,acc_hex_addr,toa_npi)
/******************************************************************/

/*
    Check an address in the context of the 'domain' type.  Zero
    return implies OK 
*/

struct xaddrf FAR *ad;
int dom, cd_cg;
int acc_hex_addr;
int toa_npi;

{
    int result;

    result = 0;				/* Initially no error */

    if( dom == LAN84 || dom == LAN88 )	
    {
	/*
	    For LAN context, called MAC must be present and length must
	    be 14 (MAC length + DSAP). Calling address not checked.
	*/
	if( cd_cg != 0 && ad->DTE_MAC.lsap_len != 14 )
	    result=1;	
    }
    else
    {
	unsigned char *addr;
	int i, j, low, msize;

	/*
	    WAN context. First check length against maximum
	    allowed for address format. For TOA/NPI in WAN88 it
	    is 17 digits all else 15 digits.
	*/
	msize = (dom == WAN88 && toa_npi ) ? DTEMAXSIZE : 15;
	    
	if( (int)ad->DTE_MAC.lsap_len > msize )
	    result = 1;

	/*
	    Check that address is BCD or HEX if configured.
	*/
	low = j = 0;
	addr = ad->DTE_MAC.lsap_add;
    
	for ( i = 0 ; i < (int)ad->DTE_MAC.lsap_len ; i++ )
	{
	    if ( low )
	    {
		low = 0;
		if( (unsigned char)(addr[j] & 15) > (unsigned char)9 && !acc_hex_addr )
		    result = 1;
		j++;
	    }
	    else
	    {
		low = 1;
		if( (unsigned char)((addr[j] >> 4) & 15)  > (unsigned char)9 && !acc_hex_addr )
		    result = 1;
	    }
	}
    }

    /*
	Check validity of any contained NSAP address
    */
    if( ad->nsap_len > 40 || (dom == WAN80 && ad->nsap_len != 0) )
	result=1;

    return(result);
}


/******************************************************************/
void x_exam_user(lsp,xvc)
/******************************************************************/

/*
    Examine events caused and enact them.
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;

{
    register int ufunction;
    int myref, cause, diag;
    mblk_t *pkt;
    bufp_t	chptr;
    struct wlcfg *cf_ptr;
    unsigned short breq;		/* For storing event */
    register struct wlcfg *xx;		/* for NUI override  */

    /*
	In this routine, the 'labort' event is assumed to have highest
	priority and all other events here appropriate may assume a
	non-NULL link to a link level/PLP database structure.
    */

    if( xvc->xusrevent!=0 )
    {
	ufunction=xfirst(xvc->xusrevent);

	switch(ufunction)
	{

	case NRSvalidation:
	/* ------------- */

	    xvc->xusrevent&= ~xNRSreply;

	    if( xvc->xstate==AskingNRS )
	    {
		if( (xvc->xflag&NRSidentified)!=0 )
		{
		    xvc->xstate=P1;		/* Go ready */
		    cause  = diag   = 0;	/* Default cause and diagnostic */

		    /*
			Map the subnetwork identifier in the called
			address to cfg
		    */
		    cf_ptr = sn_to_cfg(xvc->called.sn_id);
		    if( cf_ptr == NULL )		/* No match */
		    {
			cause = NOT_OBTAINABLE;		/* Leave diag = 0 */
			goto rej_call;
		    }
		    else
		    {
			if( (cf_ptr->SUB_MODES&BAR_OUTCALL) != 0 )
			{
			    cause = ACCESS_BARRED;	/* Leave diag = 0 */
			    goto rej_call;
			}
			else
			{
			    /* Determine the modulus of the PLP */
#ifdef MOD8ONLY
			    xvc->xmodulo = 7;
#else
			    if( cf_ptr->THISGFI == G_8 )
				xvc->xmodulo = 7;
			    else
			        xvc->xmodulo = 127;
#endif
			    /*
				Determine domain - may override this on
				address so that the PLP link domain differs
				from vc
			    */
			    xvc->xu_ident = cf_ptr->U_SN_ID;
			    xvc->xdomain = net_domain(cf_ptr);

			    if( (xvc->xdomain == WAN84 ||
			         xvc->xdomain == WAN88) &&
			        (cf_ptr->SUB_MODES & AND_80) != 0 )
			    {
				if( xvc->called.nsap_len == 0 &&
				    xvc->caller.nsap_len == 0 )
				{
				    xvc->xdomain = WAN80;
				}
			    }

			    /*
				Check up on the user supplied addresses 
				Pro tem ignore sn_id field of the caller
			    */
			    if( chk_addr(&xvc->called,xvc->xdomain,1,
		                 cf_ptr->psdn_local.SNMODES&ACC_HEX_ADD,
				 cf_ptr->SUB_MODES&SUB_TOA_NPI_FMT) != 0 )
			    {
				diag = 67;	/* Invalid called address */
			    }
			    else if( chk_addr(&xvc->caller,xvc->xdomain,0,
			             cf_ptr->psdn_local.SNMODES&ACC_HEX_ADD,
				     cf_ptr->SUB_MODES&SUB_TOA_NPI_FMT) != 0 )
			    {
				diag = 68;	/* Invalid caller address */
			    }

			    if( diag != 0 )
				goto rej_call;
			}
		    }

		    xvc->xbase = cf_ptr;	/* Address OK link cfg */

		    /*
			If NUI override subscribed, fetch associated
			facilities
		    */
		    xx=xvc->xbase;

		    if ( (xx->SUB_MODES & SUB_NUI_OVERRIDE) && (xvc->nui_len>0) )
			fetch_NUI_fac(xvc);

		    /*
			Match LSAP address to an active one if possible
		    */
		    if((lsp=match_lsap(cf_ptr,&xvc->called,xvc->xdomain))!=NULL )
		    {
			/*
			    Found one. Is it in an appropriate state?
			*/
			if( lsp->lstate!=L_disconnecting &&
			    lsp->lstate!=L_dpending )
			{
			    /*
				Queue the vc on lsap CR queue and trigger
				event
			    */
			    xvc->lsaplink = lsp;
			    xvc->xvclink  = lsp->CRqueue;
			    
			    /*
				xvc->xbase = lsp->lbase. Bases LSAP (above)
			    */
			    lsp->CRqueue = xvc;

			    if( lsp->lstate == L_connected )
				xvc->xusrevent |= xCRout;
			    /*
				Otherwise wait for the L_connected state
			    */
			}
			else
			{
			    /*
				PLP in wrong state
			    */
			    L3monitor(rjc_lstate, lsp->lu_ident);
			    diag = 0xE7;            /* NSAP unreach trans */
			    goto disclose;
			}
		    }
		    else
		    {
			/*
			    There is no match - obtain a slot and database
			*/
			if( (lsp=x25alloclsap(&myref))!=NULL )
			{
			    (void) memset((char FAR *)&lsp->slot[0],SLOTFREE,VC_SLOT_LEN);
			    lsp->ltag=PLPTAG;
			    lsp->myref =myref;
			    lsp->lstate=L_connecting;

			    /*
				Get domain - may differ from xdomain if
				80 and 84
			    */
			    lsp->ldomain = net_domain(cf_ptr);
			    lsp->lref  = map_snident(cf_ptr->U_SN_ID);

			    if( lsp->lref == (-1) )
			    {
				lfreedb(lsp);
				cause = NOT_OBTAINABLE;
				goto rej_call;
			    }

			    /*
				Copy the configuration base and subnetwork ids
			    */
			    lsp->lbase = cf_ptr;
			    xvc->xu_ident  = lsp->lu_ident = cf_ptr->U_SN_ID;
			    lsp->cfg_index = sn_to_cfg_index(lsp->lu_ident);

			    bcopy( (bufp_t)&xvc->called.DTE_MAC,
				   (bufp_t)&lsp->lladdr,
				   sizeof(struct lsapformat));

			    chptr=(bufp_t)(&lsp->lbase->local_address);

			    /*
				Request 0 service_class
			    */
			    if((diag=LL_conreq(lsp,
				               &lsp->lladdr,
					       (struct lsapformat FAR *)chptr,
					       0)) == 0 )
			    {
				/*
				    Connect request was accepted. Queue vc
				    on the lsap CR queue.
				*/
				xvc->lsaplink=lsp;
				xvc->xvclink = lsp->CRqueue;
				lsp->CRqueue = xvc;
			    }
			    else
			    {
				L3monitor(l2refusal, lsp->lu_ident);
				lfreedb(lsp);

				/*
				    Use returned diagnostic in diag
				*/
				goto disclose;
			    }
			}
			else
			{
				int static cdfk_static = 0 ;
			    /*
				L3nodb - event monitored in alloc routine
				Error- Resource constraint
			    */
				if(++cdfk_static == 1)
				{
					printf("x25alloclsap 4") ;
				}

			    diag = 163;
			    goto disclose;
			}
		    }
		}
		else
		{
		    /*
			NRS validation failed
		    */
		    L3monitor(rjc_failNRS, lsp->lu_ident);
		    diag = xvc->disdiag;
		    goto disclose;
		}
	    }
	    else
		system_error(NoNRSrequest);
		
	break;


	case UDTrequest: 
	/* ----------- */
asUDT:                                        /* Entry from masking INT */
	    xvc->xusrevent&= ~xDTout;

	    if( xvc->xstate==DataTransfer )
		Tx_Qdata(xvc);
	    else
	    {
		switch(xvc->xstate)
		{

		/*
		    Covers states where data held and enacted later
		    and states where data held and BUFreq is enacted
		*/
		case DXEbusy:
		case D2: 
		case D2pending:
		case P4pending:

		break;


		/*
		    Covers states where data is discarded only or
		    where data is discarded and BUFreq
		*/
		case WtgRCU:
		case DXEresetting:
		case DXEclearing:
#ifndef INTERNAL_DISC
		case P6:
#endif	     
		case RESUonly:
		case pRESUonly:
		case WRCUpending:
		case DXErpending:
		case DXEcpending:
		case WUNcpending:
#ifndef INTERNAL_DISC
		case P6pending:
		case DXEcfpending:
#endif
		case WUcpending:

		    tidy_user_side(xvc);

		break;
		

		/*
		    Covers all other states. Should not occur. Note return
		    to avoid bufreq
		*/
		default:

		    xusrerror(xvc,224);
		    return;

		break;
		
		}

		if( xvc->xusrevent & xBUFreq )     /* Request pending  enact */
		    goto asBUFreq;
	    }
	    
	break;


	case UEDrequest: 
	/* ----------- */
asINT:                                  /* Entry from masking INCout */
	    xvc->xusrevent&= ~xINTout;
	    
	    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy )
	    {
		if( Tx_EDdata(xvc) )	     /* Which remakes event if fails */
		{
		    if( (xvc->xusrevent&xDTout) != 0 )
			goto asUDT;
		}
	    }
	    else
	    {
		switch(xvc->xstate)
		{

		/*
		    Covers states where data held and enacted later
		    and states where data held and BUFreq is enacted
		*/
		case D2:
		case D2pending:
		case P4pending:

		break;


		/*
		    Covers states where data is discarded only or
		    where data is discarded and BUFreq
		*/
		case WtgRCU:
		case DXEresetting:
		case DXEclearing:
#ifndef INTERNAL_DISC	     
		case P6:
#endif	
		case RESUonly:
		case pRESUonly:
		case WRCUpending:
		case DXErpending:
		case DXEcpending:
		case WUNcpending:
#ifndef INTERNAL_DISC	  
		case P6pending:
		case DXEcfpending:
#endif	 
		case WUcpending:

		    tidy_user_side(xvc);

		break;

		/*
		    Covers all other states. Should not occur. Note return
		    to avoid bufreq
		*/
		default:

		    xusrerror(xvc,224);
		    return;

		break;

		}
		
		if( xvc->xusrevent & xBUFreq )		/* Request pending - enact */
		    goto asBUFreq;
	    }
	    
	break;


	case AKEDdata:
	/* -------- */

	    xvc->xusrevent&= ~xINCout;         /* Buffer needed for INC */

	    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy )
	    {
		if( xvc->xflag&INCwaited )     /* Interrupt confirm allowed? */
		{

		    if( short_out(xvc,INC_PTI)==0 )
		    {
			xvc->xusrevent|= xINCout;
		    }
		    else
		    {
			xvc->xflag &= ~INCwaited;
			if( (xvc->xusrevent&xINTout) != 0 )
			    goto asINT;
			else if( (xvc->xusrevent&xDTout) != 0 )
			    goto asUDT;
		    }
		}
		else
		    xusrerror(xvc,43);
	    }

	break;


	case URSTrequest:
	/* ----------- */

	    xvc->xusrevent&= ~xRSTout;

	    switch(xvc->xstate)                   /* NB xRST< xCAA */
	    {

	    case DataTransfer:
	    case DXEbusy:
as_dt:					/* Entry from other states */

		stop_vc_timers(xvc);
		tidy_user_side(xvc);
		into_rst_procedure(xvc,xvc->nsucause,xvc->nsudiag,WtgRCN,WtgRCNpending);
		
	    break;


	    case D2:
	    case D2pending:

		tidy_user_side(xvc);
		if( xvc->xstate==D2 )
		    xvc->xstate=WtgRCN;
		else
		    xvc->xstate=WtgRCNpending;
		goto try_BUFreq;


	    case WRCUpending:
		xvc->xstate = WtgRCNpending;
		xvc->bureason = X_RST;		/* Modify the reason */
		goto try_BUFreq;


	    case DXErpending:
	    case P4pending:

		tidy_user_side(xvc);
		
		/*
		    NB NB NB RSTout higher priority than BUFreq
		    Event wil happen when buffers available
		*/
		xvc->xusrevent |= (xRSTout); 
		goto try_BUFreq;


	    case DXEresetting:
	    case WtgRCU:
	    case RESUonly:
		goto asRSC;                  /* Collision */


	    case pRESUonly:
	    
		xvc->xusrevent&= ~xBUFreq;
		del_sqrl_data(xvc);
		goto as_dt;


#ifndef INTERNAL_DISC
	    case P6:
#endif
	    case DXEcpending:
	    case WUNcpending:
	    case WUcpending:
#ifndef INTERNAL_DISC
	    case P6pending:
	    case DXEcfpending:
#endif
try_BUFreq:                       /* Ignore the event but try the BUF */
		if( xvc->xusrevent & xBUFreq )
		    goto asBUFreq;
		else
		    break;


	    case WtgRCNpending:
	    case WtgRCN:
	    case pDTransfer:
	    default:

		xusrerror(xvc,224);

	    break;

	    }
	    
	break;


	case URSTresponse:                      /* Streams version */
	/* ------------ */

	    xvc->xusrevent &= ~xRSCout;
asRSC:                                    /* Entry as other functions */

	    if( xvc->xstate==DXEresetting || xvc->xstate==WtgRCU )
	    {
		stopT22(xvc); /* User timer */
		if( xvc->xstate==DXEresetting )
		{
		    if( Tx_RSC(xvc) !=0 )
		    {
			to_P4D1(xvc,1);             /* Enter data transfer with flow */
		    }
		    else
		    {
			need_buffer(xvc,X_RSC);
			xvc->xstate=P4pending;
		    }
		}
		else
		{
		    into_rst_procedure(xvc,0,xvc->rstdiag,D2,D2pending);
		}
	    }
	    else if( xvc->xstate == RESUonly )
	    {
		stopT22(xvc);
		to_P4D1(xvc,1);
		send_sqrl_data(xvc);
	    }

	    else if( xvc->xstate >= P6 && xvc->xstate<=DXEclearing )
	    {
		/* Do nothing except to delete the event */
	    }
	    else
	    {
		xusrerror(xvc,224);
	    }

	break;	


	case UAKs:                        /* User acks data (or credit given) */
	/* ---- */

	    xvc->xusrevent&= ~xAKout;

	    if( xvc->xstate==DataTransfer || xvc->xstate==DXEbusy )
	    {
		/*
		    Attempt to rotate the window and send an ack
		*/
		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--;
		}

		if( xvc->nrtogo != xvc->nrsent &&
		    xvc->ftoack <  xvc->rwsize &&
		    xvc->DTEbusy == 0          &&
		    xvc->ack_immediate == 0 )
		{
		    start_ackdelay(xvc);/* Merely delay the acknowledgement  */
		}
		else
		    maybe_ack(xvc);     /* Send ack if window status changes */

	    }
	    else
	    {
		/* Ignore the indication - reset states etc. */
		xvc->remcredit = 0;
	    }
	    
	break;


	case BUFrequest:                      /* Streams mods made */
	/* ---------- */

	    /* Reasons for acquiring a buffer are :

	        X_CLR - P6, P6pending,
		X_CLC - DXEclearing, DXEcfpending, R23 expired
		X_RST - D2pending, D2, WtgRCNpending, WtgRCN
		X_RSC - P4pending
		Usr_Req - all reset & clear pending states
		PVC_RSC - pDTransfer
		PVC_RES - pRESUonly
	   */
asBUFreq:                            /* Entry from masking events */
	    xvc->xusrevent &= ~xBUFreq;

	    if( xvc->bureason==X_RSC && xvc->xstate==P4pending )
	    {
		if( Tx_RSC(xvc) )
		{
		    to_P4D1(xvc,1);              /* Enter data transfer with flow */
		}
		else
		    need_buffer(xvc,X_RSC);
	    }
	    else if( xvc->bureason==X_RST &&
	             (xvc->xstate==D2pending     || xvc->xstate==D2 ||
		     xvc->xstate==WtgRCNpending || xvc->xstate==WtgRCN) )
	    {
		if( xvc->xstate==D2pending )
		{
		    into_rst_procedure(xvc,xvc->rstcause,xvc->rstdiag,D2,D2pending);
		}
		else if( xvc->xstate==WtgRCNpending )
		{
		    into_rst_procedure(xvc,xvc->rstcause,xvc->rstdiag,WtgRCN,WtgRCNpending);
		}
		else
		    cont_rst_procedure(xvc,xvc->rstcause,xvc->rstdiag,
		                                      xvc->xstate,xvc->xstate);
	    }
	    else if( xvc->bureason==Usr_Req )
	    {
		if( xvc->xstate==WRCUpending || xvc->xstate==DXErpending )
		{
		    if( reset_user(xvc,xvc->rstcause,xvc->rstdiag) )
		    {
			if( xvc->xstate == WRCUpending )
			    xvc->xstate = WtgRCU;
		    else
		    {
			/*
			    DXErpending state.  If the RST event is set then
			    this is taken as collision by upper level.  We
			    could lose data by going to DXEresetting. Check
			*/
			if( xvc->xusrevent & xRSTout )  /* Event set so */
			{			    /* P4 pending */
			    xvc->xstate = P4pending;
			    xvc->bureason   = X_RSC;
			    xvc->xusrevent &= ~xRSTout;
			    goto asBUFreq;
			}
			else
			    xvc->xstate= DXEresetting;
		    }
		}
		else
		    xvc->xusrevent|=xBUFreq;
	    }
	    else if( xvc->xstate==DXEcpending || xvc->xstate==WUNcpending ||
		     xvc->xstate==WUcpending )
	    {
		if( redisconnect_user(xvc) )   /* Using same diags and data */
		{
		    if( xvc->xstate == DXEcpending )
		    {
			xvc->xstate = DXEclearing;
			xvc->bureason=X_CLC;
			goto asBUFreq;             /* To repeat the action */
		    }
		    else if( xvc->xstate==WUNcpending )
		    {
			into_clr_procedure(xvc,0,xvc->disdiag,P6,P6pending);
		    }
		    else
		    {
			xfreedb(xvc);
		    }
		}
		else
		    xvc->xusrevent|=xBUFreq;
	    }
	    else
		L3monitor(NeednotNeeded, xvc->xu_ident);
	    }

	    else if( xvc->bureason==PVC_RES && xvc->xstate == pRESUonly )
	    {
		if( reset_user(xvc,xvc->rstcause,xvc->rstdiag) )
		{
		    /* Check for colliding reset request */
		    if( xvc->xusrevent & xRSTout )
		    {
			xvc->xusrevent &= ~xRSTout;
			to_P4D1(xvc,1);
			send_sqrl_data(xvc);
		    }
		    else
		    {
			xvc->xstate= RESUonly;
			startT22(xvc);
		    }
		}
		else
		    xvc->xusrevent |= xBUFreq;
	    }
	    else if( xvc->bureason==PVC_RSC && xvc->xstate == pDTransfer )
	    {
		if( pkt = allocb( sizeof(struct xrscf),BPRI_MED) )
		{
		    reset_confirm_user(xvc,pkt);
		    to_P4D1(xvc,1);
		    send_sqrl_data(xvc);
		}
		else
		    xvc->xusrevent |= xBUFreq;
	    }

	    else if( xvc->bureason==X_CLC && xvc->xstate==DXEclearing )
	    {
		if( short_out(xvc,CLC_PTI) )
		{
		    dec_ls_concount(lsp,xvc);
		    xfreedb(xvc);
		}
		else
		    need_buffer(xvc,X_CLC);
	    }
	    else if( xvc->bureason==X_CLR &&
	            (xvc->xstate==P6 || xvc->xstate==P6pending) )
	    {
		if( xvc->xstate==P6pending )
		{
		    into_clr_procedure(xvc,xvc->discause,xvc->disdiag,P6,P6pending);
		}
		else
		    cont_clr_procedure(xvc,xvc->discause,xvc->disdiag,P6,P6);
	    }
#ifndef INTERNAL_DISC
	    else if( xvc->bureason == X_CLC &&      /* CLC to user only          */
	            (xvc->xstate==AskingNRS ||      /* Connect not yet sent out  */
		     xvc->xstate == P1 ||           /* Connect not yet sent out  */
		     xvc->xstate == P6 ||           /* T23 timed out R23 times   */
		     xvc->xstate == DXEcfpending) ) /* Disc conf from remote end */
	    {
		if( disc_confirm_user(xvc) )
		{
		    if ( xvc->xstate==DXEcfpending )
		    {
			dec_ls_concount(lsp,xvc);
			xfreedb(xvc);               /* Setting xstate idle */
		    }
		    else if ( xvc->xstate==AskingNRS || xvc->xstate==P1 )
		    {
			xfreedb(xvc);
			if( ufunction==Udisconnect ) L3monitor(cll_uabort, xvc->xu_ident);
		    }
		}
		else
		    xvc->xusrevent|=xBUFreq;
	    }
#endif
	    else
		L3monitor(NeednotNeeded, xvc->xu_ident);

	break;


	case LSAPgone: 
	/*----------*/

	    xvc->xusrevent &= ~labort;

	    if( xvc->NSUattached != 0 )  /* Not P6,P6pend,DXEclr,Udisc etc */
	    {
		if( disconnect_user(xvc,xvc->discause,xvc->disdiag,(mblk_t *)NULL)==0 )
		{
		    xvc->xusrevent|=labort;
		    break;                   /* To try later */
		}

		L3monitor(vcs_labort, xvc->xu_ident);
	    }

	    /*
		If drop out, free the database element
	    */
	    xfreedb(xvc);

	break;


	case UCconfirm: 
	/* --------- */

	    xvc->xusrevent &= ~xCAAout;

	    /*
		In the 1984 situation, check the supplied NSAP
	    */
	    if( xvc->xstate == P3 && (int)xvc->rcalled.nsap_len > MAXASEMIOCTETS )
	    {
		xusrerror(xvc,67);		/* Invalid Called Address */
		break;
	    }

	    if( xvc->xstate != P3 )
	    {
		if( (xvc->xusrevent&xBUFreq) != 0 )
		    goto asBUFreq;
		else if( xvc->xusrevent == 0 )  /* No event pending - weird, so */
		    xusrerror(xvc,224);         /* Handle as user error */

		break;
	    }                          /* Else continue        */


	case Uconnect:
	/* -------- */

	    if( ufunction==UCconfirm )
	    {
		xvc->xusrevent &= ~xCAAout;

		if( (xvc->ISOflag&FSRRESP) != 0 )
		{
			printf("42-40") ;
		    xusrerror(xvc,42);  /* Accept not allowed */
		    return;
		}

		/*
		    Error if CONS call accept requesting ack service when
		    caller did not request ack service, or NONCONS call as
		    above and directed to clear through Dbit_control.
		*/
		if( xvc->loc_Dsupport )
		{
		    if ( !xvc->rem_Dsupport &&
		         ( xvc->CONS_call ||
		          (xvc->xbase->psdn_local.dbit_control&CC_OUT_CLR)) )
		    {
			xusrerror(xvc,0x41);
			return;
		    }
		}

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

		map_tclass(xvc,X_CAA,X_UTWARD); /* Map tclass to p/w size */
	    }
	    else
	    {
		xvc->xusrevent &= ~xCRout;

		/*
		    Check the PLP state first for restarting
		*/
		if( lsp->lstate != L_connected )
		    return;                         /* To await connection */
		    
		fix_def_facilities(xvc,X_UTWARD);       /* We know domain      */
		if( (diag=qos_available(xvc,X_UTWARD)) != 0 )
		{
		    L3monitor(L3qoscheck, xvc->xu_ident);
		    goto disclose;
		}
		map_tclass(xvc,X_CALL,X_UTWARD);          /* Map tclass to w/p */

		if( (xvc->xflag&LIM_ASSERTED) == 0 ) /* Fix nsdulength assertion */
		    xvc->nslim = (ushort) xvc->xbase->MAXNSDULEN;

		/*
		    ------- PSDN localisation mods -----------
		*/
		cf_ptr = xvc->xbase;

		/*
		    International flag search
		*/
		if( cf_ptr->psdn_local.intl_addr_recogn &&
		    xvc->called.DTE_MAC.lsap_len )

		    switch( cf_ptr->psdn_local.intl_addr_recogn )
		    {

		    default:

		    break;

		    case 1:		/* Compare DNICs */

			if( xvc->called.DTE_MAC.lsap_len >= 4 &&
			    (cf_ptr->psdn_local.dnic1 != xvc->called.DTE_MAC.lsap_add[0] ||
			    cf_ptr->psdn_local.dnic2 != xvc->called.DTE_MAC.lsap_add[1]) )
			{
			    xvc->xflag |= Intl_flag;
			}

		    break;

		    case 2:		/* Intl starts with 1 */

			if( (xvc->called.DTE_MAC.lsap_add[0]&0xF0)==0x10 )
			    xvc->xflag |= Intl_flag;

		    break;

		    case 3:		/* Intl starts with 0 */

			if( (xvc->called.DTE_MAC.lsap_add[0]&0xF0)==0x00 )
			    xvc->xflag |= Intl_flag;

		    break;

		    }		/* End switch on addr_recogn */

		    
		/*
		    Prioritise international calls?
		*/
		if( (xvc->xflag&Intl_flag) != 0   &&
		     cf_ptr->psdn_local.intl_prioritised != 0 )
		{
		    xvc->xflag |= Prty_flag;
		}
		
		/*
		    Force packet size on priority calls?
		*/
		if( (xvc->xflag&Prty_flag) != 0   &&
		    cf_ptr->psdn_local.prty_pkt_forced_value != 0 )
		{
		    xvc->lpktp = (unsigned char) xvc->rpktp = (unsigned char) cf_ptr->psdn_local.prty_pkt_forced_value;
		    xvc->lpkt  = xvc->rpkt  = (1<<xvc->rpktp);
		    xvc->xflag|= Prty_slug;
		}

		/*
		    Check the supplied CUG index with the CUG format
		    configured, ie user supplies extended index but
		    only basic format is configured.
		*/
		if( xvc->ISOflag&CUGREQ )
		{
		    unsigned char checkval;

		    if (xvc->NUI_override)
			checkval = (unsigned char) xvc->nuibase.CUG_CONTROL;
		    else
			checkval = (unsigned char) cf_ptr->CUG_CONTROL;

		    if ( xvc->cug_index[0] &&
		        (!(checkval&EXTENDED) || xvc->xdomain == WAN80) )
		    {
			printf("42-50") ;
			diag = 0x42;
			goto disclose;
		    }
		}
	    }

	    /*
		First create the packet with address and facilities
	    */
	    if( (pkt=make_c_packet(xvc,(unsigned char)(ufunction==Uconnect ? CALL_PTI : CAA_PTI)))!=NULL )
	    {
		/*
		    Resources are available for this packet.  NB the 'make'
		    routine also reserves space for headers, and appends
		    any call user data etc. to the packet
		*/
		if( ufunction==Uconnect && assign_lci(xvc)==0 )
		{
		    m_freem(pkt);
		    L3monitor(rjc_nochnl, xvc->xu_ident);
		    diag=71;                        /* No logical channel available */
		    goto disclose;                  /* To take vc off CR queue      */
		}

		if(ufunction==Uconnect)          /* Repair lcn etc               */
		{
		    chptr = (unsigned char *) mtod(pkt);
		    *(chptr)  =xvc->lgngfi | (xvc->loc_Dsupport ? 0X40 : 0X00);
	    				    /* A-bit set? */
		    *(chptr) |= ((xvc->xbase->SUB_MODES&SUB_TOA_NPI_FMT) ? 0X80 : 0X00);
		    *(chptr+1)=xvc->lcn;

#ifdef DEBUG
		    strlog(X25_STID,xvc->xu_ident,1,SL_TRACE,"CALL  Out : '%x' lci %03x gfi %x",
	                                     xvc->xu_ident,xvc->lci,*chptr>>4);
#endif
		}
		else
		{
#ifdef DEBUG
		    strlog(X25_STID,xvc->xu_ident,1,SL_TRACE,"CAA   Out : '%x' lci %03x gfi %x",
	              xvc->xu_ident,xvc->lci,(*(unsigned char *)mtod(pkt))>>4);
#endif
		}

		to_link_level(xvc,pkt);
		
		if( ufunction==Uconnect )
		{
		    xvc->xstate=P2;
		    startT21(xvc);
		    deqvc(lsp,xvc);             /* Take vc off the CR queue */
		    slot_in(lsp,xvc);           /* and replace in order     */
                    L3monitor(cll_out, xvc->xu_ident);
		}
		else
		{
		    xvc->xflag|=(caareq|vc_counted);
		    if( (lsp->concount++) == 0 ) stop_idle_timer(lsp);
		    to_P4D1(xvc,1);               /* Enter data transfer, give flow */
		    L3monitor(caa_out, xvc->xu_ident);
		}
	    }
	    else
	    {
		/* No resources, disconnect calls, but retry CAA */
		if( ufunction==Uconnect )
		{
		    L3monitor(rjc_buflow, xvc->xu_ident);
		    diag=163;
		    goto disclose;               /* To take vc off the CR queue    */
		}
		else
		    xvc->xusrevent|= xCAAout;
	    }

	    break;



	    /*
		Common code for rejection of call requests - disconnects user.
		Therej_call entry is used when cause is possible not zero
		(See NRSv)
	    */
disclose:
	    cause = 0;

rej_call:
	    if( reject_user(xvc,(unsigned char) cause,(unsigned char) diag,NULL)!=0 )        /* reject & drop through? */
		xfreedb(xvc);
	    else
	    {
		xvc->xstate = WUcpending;
		need_buffer(xvc,Usr_Req);
	    }
	
	break;


	case Uerror: 
	/* ------- */

	    xvc->xusrevent&= ~xUSRerr;
	    
	    if( xvc->NSUattached != 0 )   /* Not P6,P6pend,DXEclr,Udisc waiting */
	    {

		if( xvc->ampvc )
		    det_PVC(xvc,PVC_USRERROR);	/* better diags required */
		else
		    if( disconnect_user(xvc,xvc->discause,xvc->disdiag,(mblk_t *)NULL)==0 ) /* FAIL */
		    {
			xvc->xusrevent|=xUSRerr;     /* Reset the error event */
			break;                       /* and return later      */
		    }

		/* ELSE drop through the code for DRout */
	    }
	    
    	    /*
		Otherwise drop thru disc code, to catch err overtaking disc
		if the priorities of USRerr and Udisc are ever interchanged
	    */


	case Udisconnect:                  /* Streams version */
	/* ----------- */

	    xvc->xusrevent&= ~xDRout;

	    /* Check state */
	    if( xvc->xstate>=P2 && xvc->xstate<=DXEresetting )
	    {
		if( xvc->ampvc != 0 )		/* No pvc user - state gives action */
		{
		    breq = (xvc->xusrevent&xBUFreq);
		    flush_user(xvc,1);		/* Clear all user events */
		    xvc->nslim = 0;		/* Set the nsdu limit to zero */

		    switch( xvc->xstate )
		    {

		    /*
			States where a reset is sent to the network,
			only the diagnostic is manipulated
		    */
		    case DataTransfer:
		    case DXEbusy:
		    case pDTransfer:
		    
			xvc->rstcause=(xvc->nsucause != 0) ? (xvc->nsucause | 0x80) : 0;
			xvc->rstdiag=xvc->nsudiag;
do_rst:
			stop_vc_timers(xvc);	/* Stop all clocks */
			into_rst_procedure(xvc,xvc->rstcause,xvc->rstdiag,D2,D2pending);
			
		    break;


		    /*
			States where a reset is sent to the network, but
			the diagnostic is not manipulated
		    */
		    case D2pending    :
		    case pRESUonly    :
		    case RESUonly     :
		    case WtgRCU	      :
		    case WRCUpending  :
		    case WtgRCNpending:

			goto do_rst;


		    /*
			Reset confirm pending to network - do this
		    */
		    case P4pending	:
		    case DXErpending	:
		    case DXEresetting	:

			xvc->xusrevent&= ~xBUFreq;
			stop_vc_timers(xvc);	/* Stop all clocks */
			if( Tx_RSC(xvc) != 0 )
			{
			    to_P4D1(xvc,1);
			}
			else
			{
			    need_buffer(xvc,X_RSC);
			    xvc->xstate = P4pending;
			}
			
		    break;

		    /*
			Resetting state which changes (but bureason if
			xBUFreq is the same as the others)
		    */
		    case WtgRCN		:

			/*
			    Set state and drop through to D2 code
			*/
			xvc->xstate = D2;
			

		    /*
			Resetting states, do nothing, but reset any
			xBUFreq event which may may have already been
			set, and leave T22 alone
		    */
		    case D2		:
		    default:

			xvc->xusrevent = breq;

		    break;

		    }

		break;		/* PVC case, avoid the code below */

		}

		if( ufunction==Udisconnect && xvc->xstate==P3 )
		    L3monitor(rjc_u, xvc->xu_ident);

#ifndef INTERNAL_DISC
		/*
		    Call not established so to speed up things from the users
		    point of view confirm disc to user here.
		    If confirm routine fails then user will have to wait
		    for disc confirm from remote end.
		*/
		if( ufunction==Udisconnect && xvc->xstate==P2 )
		    disc_confirm_user(xvc); 		 /* Sets NSUattached to 0 */
#endif

		flush_user(xvc,xvc->xstate==P2 ? 1 : 0); /* Keep udiscdata? 8208 5.5.1 */
		stop_vc_timers(xvc);		/* NB NSUattached already 0 */
		into_clr_procedure(xvc,xvc->nsucause,xvc->nsudiag,P6,P6pending);
	    }

	    else if( xvc->xstate==AskingNRS || xvc->xstate==P1 ) /* CR waiting to go */
	    {
		/*
		    No disc confirm involved from remote end so confirm to user
		    here, and set glue to idle. Require to take vc off the CR queue
		    done in xfreedb.
		*/
#ifndef INTERNAL_DISC
		if( disc_confirm_user(xvc)!=0 )
		{
		    if( ufunction==Udisconnect ) L3monitor(cll_uabort, xvc->xu_ident);
		    xfreedb(xvc);
		}
		else
		{
		    need_buffer(xvc,X_CLC);
		}
#else
		if( ufunction==Udisconnect ) L3monitor(cll_uabort, xvc->xu_ident);
		xfreedb(xvc);
#endif
	    }
	    else if( xvc->xstate==AskingNRS )
	    {
		delete_NRS_request();
		if( ufunction==Udisconnect ) L3monitor(cll_uabort, xvc->xu_ident);
		xfreedb(xvc);
	    }

	    /* Now handle the clear pending states */
	    else if( xvc->xstate == DXEcpending )
	    {
		/* State change to DXEclearing then use BUFreq code */
		xvc->xstate = DXEclearing;
		xvc->bureason = X_CLC;
		goto asBUFreq;
	    }

	    else if( xvc->xstate == WUNcpending )
	    {
		/* State change to P6pending, mod diagnostic, then use BUFreq code */
		xvc->xstate = P6pending;
		xvc->disdiag=xvc->nsudiag;
		xvc->bureason = X_CLR;
		goto asBUFreq;
	    }
	    else if( xvc->xstate == WUcpending )
	    {
		/* All we can do now is to clear out the database element */
		xfreedb(xvc);
	    }
#ifndef INTERNAL_DISC
	    else if ( xvc->xstate == P6 || xvc->xstate == P6pending
	                                || xvc->xstate == DXEcfpending )
	    {
		/*
		    For states which indicate processing of a previous
		    disconnect from the user, do nothing except delete
		    the event
		*/
	    }
#endif
	    else if( ufunction != Uerror ) /* Catches case where err overtook Udis */
		system_error(UDRbad);
	
	break;

	default:
       /* ---- */

	    system_error(L3badevent);

	break;
	
	}
    }
}
