#ident "$Id: x25fac.c,v 1.2 1995/04/06 16:31:37 garyp 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
 *
 *  X25FAC.C
 *
 *
 *******************************************************************/

/*
 *	x25fac.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25fac.c
 *	@(#)x25fac.c	1.35
 *
 *	Last delta created	15:15:32 1/31/92
 *	This file extracted	18:32:54 11/19/92
 *
 */

#include "x25_inc.h"

#include "x25cause.h"
#include "x25mbu.h"
#include "x25pti.h"

#ifndef NULL
#define NULL 0
#endif

#define RPE(X) (REMOTE_PROCEDURE_ERROR<<8)  |(X)
#define IFR(X) (INVALID_FACILITY_REQUEST<<8)|(X)

/* National Options Hooks */

#define cd_net_fac(a,b,c,d)    skip_fopt(a,b,c,d)

/************************************************************************/
void fix_def_facilities(xvc,direction)
/************************************************************************/

/*
    Sets defaults when not in flags. Uses direction for extended.
*/

register struct x25vcformat FAR *xvc;
int direction;

{
    register struct wlcfg  *xx;
    unsigned char checkval1=0;
    unsigned char checkval2=0;

    xx =xvc->xbase;

    if( direction == X_UTWARD )
    {
	if (xvc->NUI_override)
	    checkval1=xvc->nuibase.SUB_MODES&SUB_EXTENDED;
	else
	    checkval1=xx->SUB_MODES&SUB_EXTENDED;

	if( (checkval1) == 0 )	
	{				
	    /*
		No extended allowed. Put to zero and fill
	    */
	    xvc->lpktp = xvc->rpktp = xvc->lwsize = xvc->rwsize = 0;
	}
    }

    /*
	Now fill in the values of the extended facilities
	Ensure we cater for the case when MAX < DEF
    */
    if( xvc->lpktp == '\0' )
    {
	if (xvc->NUI_override)
	    checkval1=(unsigned char) xvc->nuibase.LOCDEFPKTSIZE;
	else
	    checkval1=(unsigned char) xx->LOCDEFPKTSIZE;

	xvc->lpktp = (checkval1<=xx->LOCMAXPKTSIZE ? checkval1
	                                           : xx->LOCMAXPKTSIZE);
    }
    else
    {
	if( xvc->lpktp > xx->LOCMAXPKTSIZE ) xvc->lpktp = (unsigned char) xx->LOCMAXPKTSIZE;
    }
    xvc->lpkt  = (1<<xvc->lpktp);

    if( xvc->rpktp == '\0' )
    {
	if (xvc->NUI_override)
	    checkval1=(unsigned char) xvc->nuibase.REMDEFPKTSIZE;
	else
	    checkval1=(unsigned char) xx->REMDEFPKTSIZE;

	xvc->rpktp = (checkval1<=xx->REMMAXPKTSIZE ? checkval1
	                                           : xx->REMMAXPKTSIZE);
    }
    else
    {
	if( xvc->rpktp > xx->REMMAXPKTSIZE ) xvc->rpktp = (unsigned char) xx->REMMAXPKTSIZE;
    }
    xvc->rpkt  = (1<<xvc->rpktp);

    if( xvc->lwsize== '\0' )
    {
	if (xvc->NUI_override)
	    checkval1=(unsigned char) xvc->nuibase.LOCDEFWSIZE;
	else
	    checkval1=(unsigned char) xx->LOCDEFWSIZE;

	xvc->lwsize = (checkval1<=xx->LOCMAXWSIZE ? checkval1
	                                          : xx->LOCMAXWSIZE);
    }
    else
    {
	if( xvc->lwsize > xx->LOCMAXWSIZE )
	    xvc->lwsize = (unsigned char) xx->LOCMAXWSIZE;
    }

    if( xvc->rwsize== '\0' )
    {
	if (xvc->NUI_override)
	    checkval1=(unsigned char) xvc->nuibase.REMDEFWSIZE;
	else
	    checkval1=(unsigned char) xx->REMDEFWSIZE;

	xvc->rwsize = (checkval1<=xx->REMMAXWSIZE ? checkval1
	                                          : xx->REMMAXWSIZE);
    }
    else
    {
	if( xvc->rwsize > xx->REMMAXWSIZE )
	    xvc->rwsize = (unsigned char) xx->REMMAXWSIZE;
    }

    /* Fix up the TCLASS, MTCLASS defaults (incoming & outgoing calls) */

    if( (xvc->ISOflag&TCLASS) == 0 )		/* No tclass requested */
    {						/* Fix for check */
	if (xvc->NUI_override)
    	{
	    checkval1=(unsigned char) xvc->nuibase.locdefthclass;
	    checkval2=(unsigned char) xvc->nuibase.remdefthclass;
    	}
	else
    	{
	    checkval1=(unsigned char) xx->locdefthclass;
	    checkval2=(unsigned char) xx->remdefthclass;
    	}
	xvc->lthru = checkval1;
	xvc->rthru = checkval2;
    }

    if( (xvc->CCITTflag&MTCLASS)==0 )
    {						/* For qos check */
	xvc->lminthru = (unsigned char) xx->locminthclass;
	xvc->rminthru = (unsigned char) xx->remminthclass; 
    }

    /* Alter TCLASS to accomodate maximum value if CONS procedures */

    if( (xvc->ISOflag&TCLASS) != 0 )
    {
	if( xvc->lthru > xx->locmaxthclass )
	    xvc->lthru = (unsigned char) xx->locmaxthclass;
	    
	if( xvc->rthru > xx->remmaxthclass )
	    xvc->rthru = (unsigned char) xx->remmaxthclass;
	    
	/*
	    Set MTCLASS flag, since TCLASS is negotiated unless being forced
	*/
	if( xvc->CONS_call && (xvc->xflag&thruforced)==0 )
	    xvc->CCITTflag |= MTCLASS;
    }

    if( xvc->xdomain == WAN80 )
	xvc->CCITTflag|=EXPINUSE;   /* I.e. no negotiation required */
}


/************************************************************************/
void OK_wneg(wl,wr,xvc)
/************************************************************************/

/*
    Verify negotiation of window sizes and enact. l = local, r = remote
*/

unsigned char wl, wr;
register struct x25vcformat FAR *xvc;

{
    if( ( xvc->lwsize>=2 && (wl>=2 && wl<=xvc->lwsize) ) ||
        ( xvc->lwsize==1 && (wl==2 || wl==1) ) )
    {
	xvc->lwsize=wl;
    }

    if( ( xvc->rwsize>=2 && (wr>=2 && wr<=xvc->rwsize) ) ||
        ( xvc->rwsize==1 && (wr==2 || wr==1) ) )
    {
	xvc->rwsize=wr;
    }

}


/************************************************************************/
void OK_pneg(pl,pr,xvc)
/************************************************************************/

/*
    Verify negotiation of packet sizes and enact. l = local, r = remote
*/

unsigned char pl, pr;
register struct x25vcformat FAR *xvc;

{
    if( ( xvc->lpktp>=7 && (pl>=7 && pl<=xvc->lpktp) ) ||
        ( xvc->lpktp< 7 && (pl<=7 && pl>=xvc->lpktp) ) )
    {
	xvc->lpktp = pl;
	xvc->lpkt  = (1<<pl);
    }
    
    if( ( xvc->rpktp>=7 && (pr>=7 && pr<=xvc->rpktp) ) ||
        ( xvc->rpktp< 7 && (pr<=7 && pr>=xvc->rpktp) ) )
    {
	xvc->rpktp = pr;
	xvc->rpkt  = (1<<pr);
    }

}


/************************************************************************/
void map_tclass( xvc, ptype, dirn )
/************************************************************************/

/*
    If required, the throughput class maps to window and packet size.
    Supplied params are packet type and direction (going out/in).
*/

register struct x25vcformat FAR *xvc;
int ptype, dirn;

{
    register struct wlcfg       *xx;
    int is_call, is_outward, checkval=0;
    unsigned char w1, p1, m1;

    xx  = xvc->xbase;

    if( (xvc->ISOflag&TCLASS) != 0 && xx->psdn_local.thclass_type!=0 )
    {
	is_call    = ( ptype == X_CALL );
	is_outward = ( dirn  == X_UTWARD );

	switch( xx->psdn_local.thclass_type )
	{

	default:
	    /*
		Do nothing - sanity check
	    */
	break;

	/* Low nibble hack */
	case 1:

	    if(  (is_call && is_outward ) || (!is_call && !is_outward) )
		m1 = xvc->lthru;
	    else
		m1 = xvc->rthru;
		
	    goto use_m1;

	/* High nibble hack */	
	case 2:

	    if(  (is_call && is_outward ) || (!is_call && !is_outward) )
		m1 = xvc->rthru;
	    else
		m1 = xvc->lthru;
use_m1:
	    /*
		Set both directions (case 1 also) using m1
	    */
	    if( w1 = xx->psdn_local.thclass_wmap[m1] )
	    {
		xvc->lwsize = xvc->rwsize = w1;
	    }

	    if( p1 = xx->psdn_local.thclass_pmap[m1] )
	    {
		xvc->lpktp = xvc->rpktp = p1;
		xvc->lpkt  = xvc->rpkt  = (1 << p1);
	    }
	    xvc->xflag|=thruforced;           /* Leave non-negotiable */
	    
	break;

	/* Employ both to map */
	case 3:    

as_case3:
	    if( w1 = xx->psdn_local.thclass_wmap[xvc->lthru] )
		xvc->lwsize = w1;
	    
	    if( w1 = xx->psdn_local.thclass_wmap[xvc->rthru] )
		xvc->rwsize = w1;

	    if( p1 = xx->psdn_local.thclass_pmap[xvc->lthru] )
	    {
		xvc->lpktp = p1;
		xvc->lpkt  = (1 << p1);
	    }
	    if( p1 = xx->psdn_local.thclass_pmap[xvc->rthru] )
	    {
		xvc->rpktp = p1;
		xvc->rpkt  = (1 << p1);
	    }

	break;

	/* Map outgoing call values */
	case 4:    

as_case4:
	     if (xvc->NUI_override)
		checkval=xvc->nuibase.SUB_MODES&SUB_EXTENDED;
	    else
		checkval=xx->SUB_MODES&SUB_EXTENDED;
		
	    if( is_call && is_outward && (checkval) )
		goto as_case3;            /* Qos available will check/modify */
		
	break;

	/*  Case 4 + CAA use also */
	case 5:
	
	    if( is_call )
		goto as_case4;

	    if( is_outward )               /*  Is outward CAA */
	    {
		if( xvc->ISOflag&WSIZENEG )
		{
		    OK_wneg((unsigned char) xx->psdn_local.thclass_wmap[xvc->lthru],
		            (unsigned char) xx->psdn_local.thclass_wmap[xvc->rthru],xvc);
		}

		if( xvc->ISOflag&PSIZENEG )
		{
		    OK_pneg((unsigned char) xx->psdn_local.thclass_pmap[xvc->lthru],
		            (unsigned char) xx->psdn_local.thclass_pmap[xvc->rthru],xvc);
		}
	    }
	}                                 /* End switch on thclass_type */
    }
}


/************************************************************************/
int skip_fopt(findex,fl,farray,is_1980)
/************************************************************************/

/*
    Skip thru facilities to a marker.
*/

int *findex, fl, is_1980;
unsigned char *farray;

{
    unsigned char fcode, par1;

    while( (*findex)<fl )
    {
	/*
	    Check Pairs at least
	*/
	if( (fl-(*findex))<2 )
	    return(is_1980 ? RPE(64) : RPE(69));
	    
	fcode = farray[(*findex)++];
	par1  = farray[(*findex)++];
	switch( fcode & 0xC0 )
	{

	case 0x00:

	    if( fcode == 0x00 )
	    {
		/*
		    This is a marker, 'unread' it and return
		*/
		(*findex) -= 2;
		return(0);
	    }
	   
	break;

	case 0x40:

	    (*findex)++;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));
		
	break;

	case 0x80:

	    (*findex) += 2;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));
		
	break;

	case 0xC0:

	    (*findex) += par1;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));
		
	break;			

	}
    }

    return(0);
}


/************************************************************************/
int cg_net_fac(xvc,findex,fl,farray,is_call,is_1980)
/************************************************************************/

/*
    Examine calling net facilities.
*/

register struct x25vcformat FAR *xvc;
int *findex, fl, is_call, is_1980;
unsigned char *farray;

{
unsigned char fcode, par1;

    while( (*findex)<fl )
    {
	/*
	    Check pairs at least
	*/
	if( (fl-(*findex))<2 )
	    return(is_1980 ? RPE(64) : RPE(69));

	fcode = farray[(*findex)++];
	par1  = farray[(*findex)++];

	switch( fcode & 0xC0 )
	{
	case 0x00:

	    if( fcode == 0x00 )
	    {
		/*
		    This is a marker, 'unread' it and return
		*/
		(*findex) -= 2;
		return(0);
	    }
	    else if( fcode == 0x01 && xvc->xbase->NET_MODE == DATAPAC &&
		     par1  == 0x01 && is_call )
	    {
		/*
		    DATAPAC 1980 version
		*/
		xvc->xflag |= Prty_flag;
	
		if( par1 = (unsigned char) xvc->xbase->psdn_local.prty_pkt_forced_value )
		{
		    xvc->lpktp = xvc->rpktp = par1;
		    xvc->lpkt  = xvc->rpkt  = (1<<par1);
		    xvc->xflag|= Prty_slug;
		}
	    }
	    
	break;

	case 0x40:

	    (*findex)++;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));
		
	break;

	case 0x80:

	    (*findex) += 2;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));
		
	break;

	case 0xC0:

	    (*findex) += par1;
	    if( (*findex) > fl )
		return(is_1980 ? RPE(64) : RPE(69));

	break;			
	}
    }

    return(0);
}


/************************************************************************/
int get_facilities(lsp,xvc,fl,pt,cdaddr,craddr)
/************************************************************************/

/*
    Analyse the facilities field for X25 packets.  Processing
    is passed to CCITTdecode when the appropriate Facility Code
    Field is detected (thus, as per spec, the CCITT must follow ISO).
    The function is passed the packet facility length, the packet
    type (X_....) and arrays for the called/caller addresses.
    Returns 0 for no error, non-zero otherwise. 
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
int fl;
unsigned char pt, *cdaddr, *craddr;

{
    int  findex, n, i, is_a_call, sindex;
    int is_1980;
    int is_1988;
    unsigned int word;
    unsigned char fcode, par1, par2, Dlen,  semioctets;
    unsigned char m, h, mpkt, hpkt, *addptr;
    unsigned char farray[109];               /* To hold facilities */
    struct wlcfg *xx;
    unsigned char checkval1=0;
    unsigned char checkval2=0;

    xx  = xvc->xbase;

    is_a_call=(pt == X_CALL);
    
    /*
	PSS1980 etc flag, determine if to be enforced
    */

    is_1980 = ( lsp->ldomain == WAN80 ||
               (!is_a_call && xvc->xdomain == WAN80 ) );
    is_1988 = ( lsp->ldomain == WAN88 || lsp->ldomain == LAN88 ||
               (!is_a_call && xvc->xdomain == WAN88 ) ||
               (!is_a_call && xvc->xdomain == LAN88 ) );

    /*
	Get all facilities and check that not
	'Short' if data exhausted - note also, fl assumed <109
    */
    for(m=0; (int)m<fl; m++)
	if( mb_getch(xvc,(bufp_t)&farray[m])==0 )
	    return(RPE(38));

    /*
	Initialise called and calling facility lengths, priority length,
	and protection lengths. Will be set if any present.
    */
    xvc->cg_fac_len = xvc->cd_fac_len = 0; 
    xvc->prty_len   = 0;
    xvc->prot_len   = xvc->lowprot_len = xvc->protection_type = 0;

    findex=0;
    while( findex<fl )
    {
	/*
	    Facilities in pairs at least
	*/
	if( (fl-findex)<2 )
	    return(is_1980 ? RPE(64) : RPE(69));

	fcode=farray[findex++];
	par1 =farray[findex++];      /* par1 always present */

	switch( fcode&0XC0)
	{

	/* Class A facilities */
	/* ================== */
	case 0:

	    switch( fcode )
	    {
	    /* Facility Code Field */	    
	    case 0x00:
	    {
		if( par1==0X00 )
		{
		    sindex = findex;
		    if( (i = cg_net_fac(xvc,&findex,fl,&farray[0],
		                                is_a_call,is_1980)) != 0 )
			return(i);
		    else
		    {
			/*
			    Indicate calling network facilities present and
			    copy them into VC structure.
			*/
			xvc->cg_fac_len = findex - sindex;
			if ( xvc->cg_fac_len > (unsigned char)(MAX_FAC_LEN - xvc->cd_fac_len) )
			{
			    xvc->cg_fac_len = MAX_FAC_LEN - xvc->cd_fac_len;
			}

			bcopy( (bufp_t)&farray[sindex],
			       (bufp_t)(xvc->fac_field + xvc->cd_fac_len),
			       xvc->cg_fac_len );

			/*
			    Record what order the calling and called facilities
			    arrived. The user expects calling to proceed called
			    in the programmable facilities
			*/
			if ( xvc->cd_fac_len ) xvc->x_fac_len = 1;
		    
			continue;
		    }
		}
		else if( par1==0XFF )
		{
		    sindex = findex;
		    if( (i = cd_net_fac(&findex,fl,&farray[0],is_1980)) != 0 )
			return(i);
		    else
		    {
			/*
			    Indicate called network facilities present and copy
			    them into VC structure.
			*/
			xvc->cd_fac_len = findex - sindex;
			if ( xvc->cd_fac_len > (unsigned char)(MAX_FAC_LEN - xvc->cg_fac_len) )
			{
			    xvc->cd_fac_len = MAX_FAC_LEN - xvc->cg_fac_len;
			}

			bcopy( (bufp_t)&farray[sindex],
		               (bufp_t)(xvc->fac_field + xvc->cg_fac_len),
			       xvc->cd_fac_len );

			continue;
		    }
		}

		/*
		    If X.25 1980 version then no 1984/88 facilities
		    allowed
		*/
		if( is_1980 )
		{
			printf("66 - 1") ;
			return(IFR(66));

		}	

		if( par1!=0X0F )
		{
			printf("66 - 2") ;
		    return(IFR(66));          /* Facility par not allowed */
		}

		/*
		    Select conforming 1984/88 procedures
		*/
		if( is_a_call )			
		{
		    if( xvc->xdomain != LAN84 && xvc->xdomain != LAN88 ) 
			xvc->xdomain = xvc->lsaplink->ldomain;
		}
		else
		{
		    if( xvc->xdomain == WAN80 )
			{
				printf("66- 3") ;
				return(IFR(66));
			}
		}

		printf("goto CCITTDecode") ;
		goto CCITTdecode;             /* To analyse CCITT */
	    }


	    /* Fast select/reverse charging/DATAPAC 1976 */
	    case 0x01:

		/*
		    May appear only in the call packet
		    Eroor PTI incompatible 
		*/
		if( pt!=X_CALL ) 
		{
		printf("42-20") ;	
		return(RPE(42));
		}
		if( (par1 & 1) != 0 )
		    xvc->ISOflag|=REVCHARGE;
		else
		    xvc->ISOflag&=~REVCHARGE;

		if( (par1 & 0X80) !=0)
		{
		    xvc->ISOflag|=FSELECT;

		    if( (par1&0X40) != 0 )
			xvc->ISOflag|=FSRRESP;
		    else
			xvc->ISOflag&=~FSRRESP;
		}
		else
		    if( is_a_call )
			xvc->ISOflag&= ~(FSELECT | FSRRESP);

		/*
		    DATAPAC priority bit examination
		*/
		if( (par1&2) != 0 && xx->NET_MODE == DATAPAC )
		{
		    xvc->xflag |= Prty_flag;
		    if( xx->psdn_local.prty_pkt_forced_value )
		    {
			xvc->lpktp = (unsigned char) xvc->rpktp = (unsigned char) xx->psdn_local.prty_pkt_forced_value;
			xvc->lpkt  = xvc->rpkt  = (1<<xvc->rpktp);
			xvc->xflag|= Prty_slug;
		    }
		}
		
	    break;

	    /* Throughput Class Negotiation */
	    case 0x02:
	    
		xvc->ISOflag|=TCLASS;
		m=(unsigned char)(par1 & 0XF0)>>(unsigned char)4;
		h=(par1 & 0X0F);

		if( pt!=X_CALL )                  /* CAA, swap meaning */
		{
		    Dlen=m;
		    m= h;
		    h= Dlen;

		    /*
			Examine negotiation validity for thclass_neg_to_def
		    */
		    if (xvc->NUI_override)
		    {
			checkval1=(unsigned char) xvc->nuibase.locdefthclass;
			checkval2=(unsigned char) xvc->nuibase.remdefthclass;
		    }
		    else
		    {
			checkval1=(unsigned char) xx->locdefthclass;
			checkval2=(unsigned char) xx->remdefthclass;
		    }
		    
		    if( xx->psdn_local.thclass_neg_to_def )
		    {
			if( ((m>=checkval1 && m<=xvc->lthru) ||
			     (m<=checkval1 && m>=xvc->lthru) )  &&
			    ((h>=checkval2 && h<=xvc->rthru) ||
			     (h<=checkval2 && h>=xvc->rthru) ) )
			{
			    /* Negotiation is OK */
			}
			else
			    return( IFR(66) );
		    }
		    else	
		    {
			/*
			    Check thclass_type versus negotiation
			*/
			switch( xx->psdn_local.thclass_type )
			{

			/* Low nibble hack */
			case 1:

			    if( m > xx->locmaxthclass ) return(IFR(66));
			    
			break;

			/* High nibble hack */
			case 2:
			
			    if( h > xx->remmaxthclass ) return(IFR(66));
			    
			break;

			/* Both nibbles in use */
			case 3:
			
			    if( m > xx->locmaxthclass ||
			        h > xx->remmaxthclass )    return(IFR(66));

			break;

			default:
			break;

			}
		    }
		}
		else
		{
		    if( m > xx->locmaxthclass ) m = (unsigned char) xx->locmaxthclass;
		    if( h > xx->remmaxthclass ) h = (unsigned char) xx->remmaxthclass;
		}
		
		/*
		    Now mod pkt sizes etc. if required
		*/
		xvc->lthru=m;
		xvc->rthru=h;
		map_tclass( xvc, pt, X_INWARD );
		
	    break;

	    
	    /* CUG Selection (Basic)                 */
	    /* CUG Selection outgoing access (basic) */
	    case 0X03: 
	    case 0X09:
	    
		/*
		    CUG facilities only in call packet
		*/
		if( pt!=X_CALL ) { 
			printf("42-1") ;
			return(RPE(42));
		}

		if (xvc->NUI_override)
		    checkval1=xvc->nuibase.CUG_CONTROL&BAR_CUG_IN;
		else
		    checkval1=xx->CUG_CONTROL&BAR_CUG_IN;

		if( checkval1 )	
		{
			printf("65-1") ;
			return(IFR(65));
		}
		/*
		    Mark CUG present and save value in VC in extended
		    format.
		*/
		xvc->ISOflag |= CUGREQ;
		xvc->cug_index[0] = 0;
		xvc->cug_index[1] = par1;
		
	    break;


	    /* Charging Information request */
	    case 0X04:

		if (is_1980)
		{
			printf("65-2") ;
		    return(IFR(65));	  /* Not valid for 1980  */
		}
	    break;


	    /* Called Address Line Modification */                      
	    case 0x08: 

		if (is_1980)      return(IFR(65)); /* Not valid for 1980 */

		if (pt == X_CALL) 
		{
			printf("42-2") ;	
			return(RPE(42)); /* Not valid in  CALL */
		}
		xvc->called_add_mod = par1;
		
	    break;

	    default:
		printf("65-3") ;
		return(IFR(65));                 /* Code not recognised */

	    }                                /* End switch fcode for A */
	    
	break; /* End of class A facilities */

	/* Class B facilities */
	/* ================== */
	case 0X40:

	    /*
		Check par2 present
	    */	
	    if( findex>=fl )
	        return(is_1980 ? RPE(64) : RPE(69));
		
	    par2=farray[findex++];
	    switch( fcode )
	    {

	    /* Pkt size negotiation */
	    case 0X42: 

		mpkt = par1; hpkt = par2;	          /* For consistency */

		if( (par1>3 && par1<=12) &&
		    (par2>3 && par2<=12) )
		{
		    xvc->ISOflag|=PSIZENEG;

		    if( is_a_call )                   /* Indication */
		    {

			if( par1>xx->LOCMAXPKTSIZE)
			    m=(unsigned char) xx->LOCMAXPKTSIZE;      /* Accept all */
			else
			    m=par1;

			if( par2>xx->REMMAXPKTSIZE)
			    h=(unsigned char) xx->REMMAXPKTSIZE;
			else
			    h=par2;
				
			/*
			    Override the Prty slug flag since appeared
			    after
			*/
			xvc->xflag&=(~Prty_slug);
		    }
		    else if( pt==X_CAA )              /* Negotiated */
		    {
			m=par2;
			h=par1;

			/*
			    Check negotiation
			*/			    
			if( !((  (xvc->lpktp>=7
			      && (par2<=xvc->lpktp && par2>=7))
			      || (xvc->lpktp< 7
			      && (par2>=xvc->lpktp && par2<=7)))
			      &&((xvc->rpktp>=7
			      && (par1<=xvc->rpktp && par1>=7))
			      || (xvc->rpktp< 7
			      && (par1>=xvc->rpktp && par1<=7)))
			      ) )
			{
			    return(IFR(66));      /* Bad negotiation */
			}
		    }
		    else
			{
			printf("65-4") ;
			return(IFR(65));          /* Bad in this packet */
			}
		    xvc->lpktp=m;
		    xvc->rpktp=h;
		    xvc->lpkt=(1<<m);
		    xvc->rpkt=(1<<h);
		}
		else
		    return(IFR(66));
		    
	    break;


	    /* Window size negotiation */
	    case 0X43: 

#ifdef MOD8ONLY
		m = 7;
#else
		m = ( xvc->xbase->THISGFI == G_8 ? 7 : 127 );
#endif
		if( par1<=m && par2<=m )  /* Maybe extended */
		{
		    if( is_a_call )               /* Indication */
		    {
			/*
			    Accept window size 1 - following if commented out
			    if( (m=par1)==1 ) m=2;      2 always available
			*/
			m=par1;
			if( m > xx->LOCMAXWSIZE ) m=(unsigned char) xx->LOCMAXWSIZE;

			/* if( (h=par2)==1 ) h=2; */
			h=par2;
			if( h > xx->REMMAXWSIZE ) h=(unsigned char) xx->REMMAXWSIZE;
		    }
		    else if( pt==X_CAA )
		    {
			m=par2;
			h=par1;

			/*
			    Check negotiation
			*/
			if( !( ((xvc->lwsize>=2
			         && (par2<=xvc->lwsize && par2>=2))
			     || (xvc->lwsize==1
			         && (par2==1 || par2==2)))
			     && ((xvc->rwsize>=2
			         && (par1<=xvc->rwsize && par1>=2))
			     || (xvc->rwsize==1
			         && (par1==1 || par1==2))) ) )
			{
			   return(IFR(66));            /* Bad negotiation */
			}
		    }
		    else
			{
			printf("65-5") ;
			return(IFR(65));              /* bad packet */
			}
		    xvc->ISOflag|=WSIZENEG;
		    xvc->lwsize=m;
		    xvc->rwsize=h;
		}
		else
		    return(IFR(66));
		    
	    break;


	    /* Transit delay negotiation */
	    case 0X49: 

		xvc->ISOflag|=TRDELAY;
		xvc->ltrans  =(par1<<8)|par2;

	    break;


	    /* CUG selection (extended) */
	    /* CUG selection outgoing access (extended) */
	    case 0X47: 
	    case 0X48:

		if( is_1980 )
		{
			printf("65-6") ;
		    return(IFR(65));     /* Extended not 1980 */
		}

	    /* BCUG selection */	
	    case 0X41:
	    
		if( pt!=X_CALL ) 
		{
			printf("42-3") ;	
			return(RPE(42));
		}
		if( xx->CUG_CONTROL&BAR_CUG_IN )
		{
			printf("65-7") ;
		    return(IFR(65));
		}
		/*
		    Mark CUG/BCUG present and save value in VC. Note
		    two bytes for extended format.
		*/
		xvc->ISOflag |= (fcode == 0x41) ? BCUGREQ : CUGREQ;
		xvc->cug_index[0] = par1;
		xvc->cug_index[1] = par2;
		
	    break;


	    /* RPOA selection (basic) */
	    case 0X44: 

		if( pt!=X_CALL ) 
		{
		printf("42-21") ;	
		return(RPE(42));  /* Only in call packet */
		}
	    break;

	    default:
		printf("65-8") ;
		return(IFR(65));
		
	    }  /* End fcode switch on B facilities */

	break; /* End of class B facilities */


	/* Class C facilities */
	/* ================== */
	case 0X80:

		printf("65-9") ;
	    return(IFR(65));         /* There are none */

	/* End of class C facilities */


	/* Class D facilities */
	/* ================== */
	case 0XC0:

	    Dlen=par1;               /* par1 contains byte length */

	    if( (fl-findex)<(int)Dlen )
		return(is_1980 ? RPE(64) : RPE(69));

	    /*
		Current recognised Class D facilities
		are not applicable for 1980
	    */
	    if (is_1980)
	    {
	   		printf("65-10") ; 
	    	return(RPE(65));
		}

	    switch( fcode )
	    {

	    /*
	        Network User Identifier - Call Setup Only
	    */
	    case 0XC6:

		if( (pt != X_CALL) && (pt != X_CAA) )
		{
			printf("42-3") ;
		    return(RPE(42));
		}
		findex+=Dlen;               /* Skip over */
		
	    break;


	    /*
	        Charging - Indicating Monetary Unit - Clearing Only
	    */
	    case 0XC5:

		if( (pt != X_CLR) && (pt != X_CLC) )
		{
			printf("42-4") ;
		    return(RPE(42));
		}

		xvc->ch_mu_len = ( (int)Dlen > MAX_MU_LEN ) ? MAX_MU_LEN : Dlen;
		bcopy(&farray[findex], xvc->ch_mu_field, xvc->ch_mu_len);

		findex+=Dlen;               /* Skip over */
		
	    break;
	    

	    /*
	        RPOA selection (extended) - Call packets only
	    */
	    case 0XC4:

		if( pt!=X_CALL ) 
		{
			printf("42-5") ;	
			return(RPE(42));
		}

		findex+=Dlen;                     /* Skip over */

	    break;


	    /*
	        Call redirection - Call packets only
	    */
	    case 0XC3:

		if( pt!=X_CALL ) 
		{
			printf("42-6") ;	
			return(RPE(42));
		}

		xvc->call_redirect = farray[findex];
		xvc->orig_add.DTE_MAC.lsap_len = farray[findex+1];
		bcopy(&farray[findex+2],
		       xvc->orig_add.DTE_MAC.lsap_add, LSAPMAXSIZE);

		findex+=Dlen;                     /* Skip over */

	    break;


	    /*
	        Charging - Indicating Segment Count - Clearing Only
	    */
	    case 0XC2:

		if( (pt != X_CLR) && (pt != X_CLC) )
		{
			printf("42-7") ;
		    return(RPE(42));
		}

		xvc->ch_sc_len = ( (int)Dlen > MAX_SC_LEN ) ? MAX_SC_LEN : Dlen;
		bcopy(&farray[findex], xvc->ch_sc_field, xvc->ch_sc_len);

		findex+=Dlen;                     /* Skip over */

	    break;


	    /*
	        Charging - Indicating Call Duration - Clearing Only
	    */
	    case 0XC1:

		if( (pt != X_CLR) && (pt != X_CLC) )
		{
			printf("42-8") ;
		    return(RPE(42));
		}

		xvc->ch_cd_len = ( (int)Dlen > MAX_CD_LEN ) ? MAX_CD_LEN : Dlen;
		bcopy(&farray[findex], xvc->ch_cd_field, xvc->ch_cd_len);

		findex+=Dlen;                     /* Skip over */
		
	    break;


	    default:
		printf("65-11") ;
		return(IFR(65));
		
	    }  /* End switch on fcode for D facilities */
	    
	break; /* End of class D facilities */

	}  /* End switch on fcode top 2 bits */

    } /* End while on facility length */

    goto consistency_test;                 /* OK - test consistency */

    /*
	End ISOdecode part of the facilities
    */
CCITTdecode:

    /*
	CCITTdecode - for X25 1984 CCITT DTE facilities
    */
    while( findex<fl )
    {
	if( (fl-findex)<2 ) return(RPE(69));
	
	fcode=farray[findex++];
	par1 =farray[findex++];

	switch( fcode&0XC0)
	{

	/* Class A facilities */
  	/* ================== */
	case 0:  

	    switch( fcode )
	    {

	    /* Expedited data negotiation */
	    case 0x0B:

		if( par1==0 || par1==1 )
		{
		    if( is_a_call )
		    {
			xvc->CCITTflag|=EXPSELECTED;
			    
			if( par1==1 )
			    xvc->CCITTflag|= EXPDATANEG;
			else
			    xvc->CCITTflag&=~EXPDATANEG;
		    }
		    else if( pt==X_CAA )
		    {
			/*
			    Negotiating use of exp data. Class error as
			    'use' when none requested
			*/
			if( par1==1 )
			{

			    if( (xvc->CCITTflag&(EXPSELECTED|EXPDATANEG))
				              == (EXPSELECTED|EXPDATANEG) )
			    {
				xvc->CCITTflag|=EXPINUSE;
			    }
			    else
				return(IFR(77)); /* Invalid negotiation */
			}
			else
			    xvc->CCITTflag&= ~(EXPDATANEG|EXPINUSE);
		    }
		    else
			return(IFR(77));
		}
		else
		    return(IFR(77));    /* CCITT code/param not allowed/invalid */

	    break;


	    /* Minimum Throughput Class Negotiation */
	    case 0x0A:

		if( pt!=X_CALL )
		{
			printf("42-9") ;	
			return(RPE(42));  /* call packet only */
		}

		xvc->CCITTflag|=MTCLASS;
		m=(unsigned char)(par1 & 0XF0)>>(unsigned char)4;
		h=(par1 & 0X0F);

		if( pt!=X_CALL )                   /* CAA, swap meaning */
		{
		    Dlen=m;
		    m= h;
		    h= Dlen;
		}

		xvc->lminthru=m;
		xvc->rminthru=h;
		    
	    break;

	    default:
		
		return(IFR(77));     /* Facility code not recognised */

	    }                        /* End switch fcode for A       */
		
	break; /* End of class A facilities */


	/* Class B facilities */
	/* ================== */
	case 0X40:

	    return(IFR(77));         /* There are none */

	/* End of class B facilities */


	/* Class C facilities */
	/* ================== */
	case 0X80:

	    return(IFR(77));        /* There are none */
		
	/* End of class C facilities */



	/* Class D facilities */
	/* ================== */
	case 0XC0:

	    Dlen=par1;                    /* Byte length in par1 */

	    if( Dlen==0 ) return(IFR(77));
		
	    if( (fl-findex)<(int)Dlen ) return(RPE(69));
		
	    switch( fcode )
	    {

	    /* End to end transit delay */
	    case 0XCA: 

		if( Dlen==2 || (is_a_call && (Dlen==4 || Dlen==6)) )
		{
		    for(m=2;m<=Dlen;m+=2)
		    {
			par1=farray[findex++];
			par2=farray[findex++];
			word=(par1<<8)|par2;

			switch(m)
			{

			case 2:

			    xvc->CCITTflag|=ENDTOEND;
			    xvc->etoecumulative=(ushort)word;
printf("cum word = %d\n");
				    
			break;


			case 4:

			    xvc->etoetarget=(ushort)word;
printf("cum word = %d\n");
			    xvc->CCITTflag|=ENDTARGET;

			break;

			case 6:
			    
			    xvc->etoemaxaccept=(ushort)word;
			    xvc->CCITTflag|=ENDMAX;
				
			break;

			}
		    }
		}
		else
		    return(IFR(77));
			
	    break;


	    /* Calling Address Extension */
	    case 0XCB:

		/*
		    Facility appears only in call and clear(88) packets
		*/
		if( pt!=X_CALL && !(pt==X_CLR && is_1988) )
		{
			printf("42-10") ;
		    return(RPE(42));
		}
		addptr=(&craddr[0]);
		goto getadd;


	    /* Called Address Extension */
	    case 0XC9:

		/*
		    Facility may not appear in clear confirmation
		*/
		if( pt==X_CLC )
		{
			printf("42-11") ;	
			return(RPE(42));
		}
		addptr=(&cdaddr[0]);

getadd:		/*
		    Common code for address acquisition
		    On entry here, it is already checked that Dlen is
		    not zero and that the Dlen bytes are present
		*/
		    
		par2=farray[findex++];        /* Field length & param */

		if( (m=(par2&0xC0)) == 0XC0 ) return(IFR(77));

		/*
		    Partial NSAP treated as an address extension
		*/
		if( m )
		    xvc->CCITTflag|=EXTDADDR;
			
		if( (int)(semioctets= par2&0X3F) > MAXASEMIOCTETS )
		    return( (fcode==0XC9) ? RPE(67) : RPE(68) );
			
		/*
		    Field length is n+1 where n should be (semi+1)/2
		*/
		n=Dlen-1;

		if( n!=((int)(semioctets+1)>>1) )
		    return(IFR(77));           /* Inconsistent */

		/*
		    Now load the address from the facilities field
		    the facility length is OK
		*/
		for(i=1; i<=n; i++)
		{
		    addptr[i]=farray[findex++];
		}
		addptr[0] = semioctets;

	    break;


	    /*
		Call Priority
	    */
	    case 0XD2:

		if (!is_1988)
		{	
			printf("65-12") ;				/* 1988 facility only */
		    return(IFR(65));
		}
		/*
		    Check length of priority field
		    Note that max priority length in non-call
		    request packets is 3
		*/
		if (((int)Dlen > MAX_PRTY_LEN) || (pt != X_CALL && Dlen > 3))
		{
			printf("42-13") ;
		    return(RPE(42));
		}
		xvc->prty_len = Dlen;
		bcopy(&farray[findex], xvc->prty, xvc->prty_len);

		findex+=Dlen;                     /* Skip over */
		    
	    break;


	    /*
		Call Protection
	    */
	    case 0XD3:
		
		if (!is_1988)
		{	printf("65-13") ;					/* 1988 facility only */
		    return(IFR(65));
		}
		xvc->protection_type = (farray[findex++] >> 6);
		xvc->prot_len        = farray[findex];
		    
		findex++;
		bcopy(&farray[findex], xvc->protection, xvc->prot_len);
		    
		findex+=xvc->prot_len;                 /* Skip over */

		if ((int)Dlen > (int)(xvc->prot_len + 2))
		{
		    xvc->lowprot_len = farray[findex];
		    findex++;

		    /*
			If protection lengths too long, reject call
		    */
		    if (xvc->prot_len > MAX_PROT ||
			xvc->lowprot_len > MAX_PROT)
		    {
			/*
			    Reset protection in VC
			*/
			xvc->protection_type = 0;
			xvc->prot_len = 0;
			xvc->lowprot_len = 0;
			printf("42-14") ;
			return(RPE(42));
		    }

		    if (xvc->lowprot_len)
		    {
			/*
			    Low protection only in call packets
			*/
			if( pt!=X_CALL )
			{
			    /*
				Reset protection in VC
			    */
			    xvc->protection_type = 0;
			    xvc->prot_len = 0;
			    xvc->lowprot_len = 0;
				printf("42-15") ;
			    return(RPE(42));
			}

			bcopy(&farray[findex], xvc->lowprotection,
		    	                         xvc->lowprot_len); 

			findex+=xvc->lowprot_len;         /* Skip over */
		    }
		}

		/*
		    Check that the protection lengths are consistent with
		    the facility length.
		*/
		if (xvc->lowprot_len && (Dlen != (xvc->lowprot_len +
		                                  xvc->prot_len + 3)))
		{
		    /*
			Reset protection in VC
		    */
		    xvc->protection_type = 0;
		    xvc->prot_len = 0;
		    xvc->lowprot_len = 0;

		    return(is_1980 ? RPE(64) : RPE(69));
		}

	    break;

	    default:

		return(IFR(77)); /* Invalid specification for CCITT facility */
		    
	    }  /* End switch on fcode for D facilities */
		
	break; /* End of class D facilities */
	    
	}  /* End switch on fcode top 2 bits */
	    
    } /* End while on facility length */

    /*
	End CCITTdecode part OK - fall through consistency test
    */
consistency_test:

    if (xvc->NUI_override)
	checkval1=xvc->nuibase.SUB_MODES&BAR_EXTENDED;
    else
    	checkval1=xx->SUB_MODES&BAR_EXTENDED;

    if( is_a_call )
    {
	if( (xx->SUB_MODES&SUB_FSELECT) == 0  &&
	    (xvc->ISOflag&FSELECT) != 0 )
	{
	    return(IFR(66));
	}
	
	if( (xvc->ISOflag&FSRRESP)!=0  )
	{
	    if( (xx->SUB_MODES&SUB_FSRRESP) == 0 ||
	        (xvc->ISOflag&(PSIZENEG|WSIZENEG|TCLASS))!=0 )
	    {
		printf("65-14 %x %x",xx->SUB_MODES, xvc->ISOflag) ;
		return(IFR(65));
	    }
	}
	if( (xvc->ISOflag&REVCHARGE)!=0 &&
	    ((xx->SUB_MODES&SUB_REVCHARGE) == 0 ||
	    (xx->SUB_MODES&SUB_LOC_CHG_PREV) != 0))
	{
	    return(IFR(66));
	}
	
	if( (checkval1) != 0  &&
	    (xvc->ISOflag&(PSIZENEG|WSIZENEG))!=0 )
	{
		printf("65-15") ;
	    return(IFR(65));
	}
	
	if( xvc->xdomain == WAN80 && (xvc->ISOflag&PSIZENEG) != 0 )
	{
	    if( mpkt > 10 || hpkt > 10 )
		return(IFR(66));
	}
    }
    else if( pt == X_CAA )
    {
	if( (checkval1) != 0  &&
	    (xvc->ISOflag&(PSIZENEG|WSIZENEG))!=0 )
	{
		printf("65-16") ;
	    return(IFR(65));
	}
    }

   return(0);                    /* OK */
}


/************************************************************************/
static int geta(xvc,acd,acg)
/************************************************************************/

/*
    Internal routine to acquire X.121 addresses.  Not
    optimal but compact. 0 success/38 read failed
*/

register struct x25vcformat FAR *xvc;
unsigned char *acd, *acg;

{
    unsigned char total;
    unsigned char puttop;
    unsigned char bytgot;
    unsigned char i, nibble;
    unsigned char *arrptr;
    int bcd_fail;

    /*
	Initialise the get/put variables
    */

    total = ( acd[0] + acg[0] );         /* Total number of nibbles      */
    puttop=1;                            /* Put top nibble               */
    bcd_fail=67;                         /* Inv called address           */
    arrptr=(&acd[1]);                    /* Called address first         */

    for( i=0; i < total; i++)
    {
	if( i==acd[0] )                  /* Just past called address     */
	{
	    bcd_fail=68;                 /* Inv calling address          */
	    arrptr=(&acg[1]);            /* Over to calling address now  */
	    puttop=1;                    /* And put into top nibble      */
	}

	/*
	    If we have no byte, then get one
	*/
	if( (i&1)==0 )                   /* Need a byte                  */
	{
	    if( mb_getch(xvc,(bufp_t)&bytgot)==0 )
		return(RPE(38));
		
	    nibble = ((bytgot>>4)&15);
	}
	else
	{
	    nibble = ((bytgot   )&15);   /* Discard this byte now        */
	}

	/*
	    Check the nibble is BCD or hex if allowed
	*/
	if( nibble > 9 && !(xvc->xbase->psdn_local.SNMODES&ACC_HEX_ADD) )
	    return(bcd_fail);

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

    return(0); /* OK */
}

/************************************************************************/
int pkt_details(lsp,xvc,pt,inpkt,Abitin)
/************************************************************************/

/*
    pkt_details - examines pkts carrying addresses etc.
                  returns non-zero on error.
*/

struct lsformat FAR *lsp;
register struct x25vcformat FAR *xvc;
unsigned char pt;
mblk_t *inpkt;
int Abitin;
{
    unsigned char wrk1, wrk2, facl;
    unsigned int  i,j,totl,fres,Abitset;

    unsigned char cdadd[MAXAOCTETS+1],
                  cradd[MAXAOCTETS+1];   /* To capture NSAP addresses */
    unsigned char cd_SNPA[LSAPMAXSIZE+1],
                  cr_SNPA[LSAPMAXSIZE+1];/* To capture SNPA addresses */
    struct xaddrf FAR *adptr;

    totl = mb_bulen(inpkt);

    /*
	Check for A-bit set when TOA/NPI Barred.
	No check for A-bit not set when TOA/NPI is subscribed to.
	(ISO 8208 (88)"TOA/NPI for further study")
    */
    if (Abitin && (xvc->xbase->SUB_MODES&BAR_TOA_NPI_FMT))
	return(RPE(172));

    /*
	Extract the SNPA address lengths if present.
	NB. The call to mb_getch returns 0 if pkt empty.
    */
    i = mb_getch(xvc,(bufp_t)&wrk1);
    j = 0;

    /*
	TOA/NPI format uses two bytes.
    */
    if (Abitin)
    {
	j = mb_getch(xvc,(bufp_t)&wrk2);

	/*
	    Pkt short if both bytes not present
	*/
	if ( i && j == 0 )
	    return(RPE(38));
    }

    /*
	Check the overall lengths of the buffer against the pkt type.
    */
    if (pt == X_CALL && totl < 2)
	return(RPE(38));                       /* Short */

    else if( pt==X_CLC )
    {
	/*
	    For CLC the address lengths if present should be zero.
	*/
	if( i )
	{
	    if( wrk1 != 0 ) return(RPE(74)); 
	    if( j && wrk2 != 0 ) return(RPE(74));
	}
	else
	    return(0);                     /* Packet is OK (basic) */
    }

    else if ( ( pt == X_CLR) || ( pt==X_CAA ) )
    {
	/*
	    If i is set to 0 then pkt is empty so no need to
	    check second byte for TOA/NPI
	*/
	if( i == 0 ) return(0);               /* Packet OK (basic)     */
    }

    totl--;                                   /* Count out the add len */

    if (Abitin)
	totl--;			      /* Extra byte for TOA/NPI format */

    /*
	Tidy capture by clearing out the SNPA addresses
    */
    memset((bufp_t)&cd_SNPA[0],0,LSAPMAXSIZE+1);
    memset((bufp_t)&cr_SNPA[0],0,LSAPMAXSIZE+1);

    /*
	Now get the SNPA addresses (zero lengths OK here)
    */
    if (Abitin)
    {
	i = cd_SNPA[0] = wrk1;		/* TOA/NPI format- 1 octet */
	j = cr_SNPA[0] = wrk2;		/*      per address length */
    }
    else
    {
	i = cd_SNPA[0] = (wrk1&0X0F);
	j = cr_SNPA[0] = (wrk1>>4)  ;
    }
    totl -= ((i+j+1)>>1);

    if( (i = geta(xvc,&cd_SNPA[0],&cr_SNPA[0])) != 0 )
	return(RPE(i));                             /* Packet err */

    /*
	The address is unloaded, get the facility length (if present)
    */
    if( mb_getch(xvc,(bufp_t)&facl) == 0 )
    {
	if( pt == X_CALL )
	    return(RPE(38));            /* Short */
	else                            /* Others */
	    return(0);                  /* Extreme generosity */
    }
    totl-=1;

    /*
	The maximum facility length possible is 109.
	Check this vs 1980 etc
    */
    if( facl>109 || (facl>63 && lsp->ldomain == WAN80) )
	return(RPE(69));			       /* Invalid length */

    if( facl > totl) return(RPE(64));                  /* Short */

    /*
	Now check the facilities. Note that a potential NSAP length
        of zero is already set in the memset initialisation
    */
    memset((bufp_t)&cdadd[0],0,MAXAOCTETS+1);
    memset((bufp_t)&cradd[0],0,MAXAOCTETS+1);

    if((fres = get_facilities(lsp,xvc,(int)facl,pt,&cdadd[0],&cradd[0])) == 0 )
    {
	/* PSS etc. post check facility length */
	if( xvc->xdomain == WAN80 && facl > 63 ) return(RPE(69));

	if( (totl-=facl) != 0 )
	{
	    if(pt == X_CLC || totl>128 ||
	       ((xvc->ISOflag&FSELECT) == 0 && (totl > 16 || pt != X_CALL)))
	    {
		return(RPE(39));    /* Long */
	    }
	}

	/*
	    All seems well so far, check and copy the addresses, caller 1st
	*/
	if( pt == X_CALL )
	{
	    adptr = (&xvc->caller);
	    adptr->sn_id = xvc->xu_ident;
	    adptr->aflags = ((xvc->CCITTflag&EXTDADDR) ? EXT_ADDR : NSAP_ADDR);
	    adptr->nsap_len = cradd[0];
	    bcopy( (bufp_t)&cradd[1], (bufp_t)&adptr->NSAP[0],MAXAOCTETS);

	    /*
		For WAN case, we also require the callers DTE
	    */
	    if( xvc->xdomain != LAN84 && xvc->xdomain != LAN88 ) 
	    {
		adptr->DTE_MAC.lsap_len = cr_SNPA[0];
		bcopy( (bufp_t)&cr_SNPA[1],
		       (bufp_t)&adptr->DTE_MAC.lsap_add[0],
		        LSAPMAXSIZE);
	    }
	    adptr = (&xvc->called);
	}
	else        /* Other packets, only interested in responder */
	{
	    adptr = (&xvc->rcalled);
	}

	/*
	    Fill in the flag parts of called or responder address
	*/
	adptr->sn_id = xvc->xu_ident;
	adptr->aflags = ( (xvc->CCITTflag&EXTDADDR) ? EXT_ADDR : NSAP_ADDR);

	if( pt==X_CALL || ( cd_SNPA[0] != 0 || cdadd[0] != 0 ) )
	{
	    adptr->nsap_len = cdadd[0];
	    bcopy( (bufp_t)&cdadd[1], (bufp_t)&adptr->NSAP[0],MAXAOCTETS);

	    /*
		For WAN case, we also require the called DTE
	    */
	    if( xvc->xdomain != LAN84 && xvc->xdomain != LAN88 ) 
	    {
		adptr->DTE_MAC.lsap_len = cd_SNPA[0];
		bcopy( (bufp_t)&cd_SNPA[1],
		       (bufp_t)&adptr->DTE_MAC.lsap_add[0],
		       LSAPMAXSIZE);
	    }
	}
    }

    /* Drop through here to return 'fres' */
    return(fres);                   /* Error in facilities or zero */
}

