#ident "$Id: x25upper.c,v 1.6 1997/09/24 23:13:51 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
 *
 *  X25UPPER.C
 *
 *
 *******************************************************************/

/*
 *	x25upper.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25upper.c
 *	@(#)x25upper.c	1.69
 *
 *	Last delta created	13:16:24 8/26/92
 *	This file extracted	18:33:07 11/19/92
 *
 */

/* ----------------------------------------------------------------------
    	    UPPER OPEN, READ/WRITE PUT & SERVICE PROCEDURES
          uses the PLP interface procedures in x25nsuif.c
   ---------------------------------------------------------------------- */

#define	STRMUSER	u.u_uid

#include "x25_inc.h"

#include "errno.h"
#include "system.h"

/* #include "sysmacros.h" */
#include "iso.h"
#include "x25cause.h"
#include "x25form.mac"
#include "x25aux.h"

#ifndef NULL
#define NULL 0
#endif

extern unsigned long curtime();

/************************************************************************/
void abort_vc(xug)
/************************************************************************/

/*
    Routine to initiate internal disconnect within the X.25 code. Calls
    the (x25userev) function xusrerror to set the user error event.
    The event will be handled at next clock tick.
*/

struct xgldbf *xug;

{
    struct x25vcformat FAR *xvc;

    L3monitor(xifaborts, 0);

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

	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
	{
	    xusrerror(xvc,X25_ABORT);
	}
    }
}


/************************************************************************/
void rmXLDAT(q)
/************************************************************************/

/*
    Used to remove data messages from a queue
*/

queue_t *q;

{
    mblk_t *temp, *tempx;
    int i, s;

    s=qsize(q);
    temp = q->q_first;

    for( i=0; i<s; i++)
    {
	if( ((struct xdataf FAR *)temp->b_rptr)->xl_type == XL_DAT )
	{
	    tempx = temp->b_next;
            rmvq(q,temp);
	    freemsg(temp);
	    temp = tempx;
	}
	else
	    temp = temp->b_next;
    }
}


/************************************************************************/
void rmCIDI(xug)
/************************************************************************/

/*
    Progressively remove CI & DIs awaiting on the listener
    Used when close or crisis.
*/

struct xgldbf *xug;

{
    mblk_t *glrec;

    while( (glrec = OFF(&xug->gdbq)) != NULL )
    {
	if( not_abort(glrec) )
	{
	    if( N_disreq( ((struct i_xcallf FAR *)glrec->b_rptr)->holdns,
	    	 0x00,
		 0xE7,		/* NSAP unreach transient */
		 NULL,
		 (struct xaddrf FAR *)NULL,
		 (struct xaddrf FAR *)NULL,
		 NULL) != 0 )	/* Sys error actually */
	    {
		strlog(X25_STID,0,0,SL_ERROR,"X25: abort DI ref match");
	    }
#ifndef INTERNAL_DISC
	    else
	    {
		/*
		    Stream closing so no user to send N_DC to
		*/
		iso_vc[((struct i_xcallf FAR*)glrec->b_rptr)->holdns]->NSUattached=0;
	    }
#endif
	}
	freemsg(glrec);			/* Free the record */
    }
    xug->g_earuse = 0;
}


/************************************************************************/
void go_frozen(q,mp)
/************************************************************************/

/*
    Routine to send up M_ERROR message and to set any db element to
    FREEZING/FROZEN - used for user errors in message specification
    and i/f errors.
*/

queue_t *q;					/* Upper write queue */
mblk_t  *mp;

{
    struct xgldbf *xug;

    if( xug = (struct xgldbf *) q->q_ptr )
    {
	if( xug->gstate != G_FROZEN )
	{
	    if( xug->gstate == G_LISTENING )	/* FREE RESOURCE */
		rmCIDI(xug);
	    if( xug->pvcflg )			/* FREE PVC RESOURCE */
	    {
		xug->pvcflg = 0;
		xpurgeQ((struct Q FAR *)&xug->g_ear);
		xpurgeQ((struct Q FAR *)&xug->gdbq);
	    }

	    if( pr_length(mp) >= 1 )		/* Can use the buffer */
	    {
		if( mp->b_cont )
		{
		    freemsg(mp->b_cont);
		    mp->b_cont = NULL;
		}
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_rptr) = EINVAL;
		mp->b_wptr = mp->b_rptr + 1;
		putnext(RD(q),mp);
		mp = NULL;
		xug->gstate = G_FROZEN;
	    }
	    else
	    {
		if( putctl1(RD(q)->q_next,M_ERROR,EINVAL) )
		    xug->gstate = G_FROZEN;
		else
		    xug->gstate = G_FREEZING;
	    }
	}
    }

    if( mp )
	freemsg(mp);
}


/************************************************************************/
mblk_t *scan_ears(con, xug )
/************************************************************************/

/*
    Scans a listener looking for queued record with this connid.
*/

int con;
struct xgldbf *xug;

{
    int j;
    mblk_t *rec;

    /*
	g_earuse is the number of entries on the gdbq queue
    */
    for( j=0; j<xug->g_earuse; j++ )
    {
	rec = OFF(&xug->gdbq);
	if( (((struct xcallf FAR *)rec->b_rptr)->xl_command == N_CI &&
	     ((struct xcallf FAR *)rec->b_rptr)->conn_id    == con ) ||
	     (((struct xdiscf FAR *)rec->b_rptr)->xl_command == N_DI &&
	     ((struct xdiscf FAR *)rec->b_rptr)->conn_id    == con ) )
	{
	    xug->g_earuse--;
	    return( rec );
	}
	else
	    ON(&xug->gdbq,rec);
    }
    return(NULL);				/* NO MATCH!!!! */
}


/************************************************************************/
mblk_t *find_connid(con, retindex)
/************************************************************************/

/*
    Scans listeners looking for queued record with this connid. Uses
    scan_ears to unqueue the record and returns pointer to the record
    and glue index
*/

int con;
int *retindex;

{
    int i;
    struct xgldbf *xug;
    mblk_t *rec;

    for(i=0; i<x25devmax; i++)
    {
	xug = (&xgldb[i]);
	if(xug->gstate == G_LISTENING && xug->g_earuse != 0 )
	{
	    if( (rec = scan_ears(con, xug)) != NULL )
	    {
		*retindex = i;
		return( rec );
	    }
	}
    }
    return(NULL);					/* NO MATCH!!!! */
}


/************************************************************************/
void pvc_fn_err(xug)
/************************************************************************/

/*
    Called when user sends an interface function invalid for a
    PVC (eg. N_DI).
*/

struct xgldbf *xug;

{
    mblk_t *mp;

    /*
	Set user error or close. Only 2 states possible
    */
    if( xug->gstate == G_ATTACHED )
	abort_vc(xug);
    else if( xug->gstate == G_PVCWAITING )
    {
	mp = OFF(&xug->g_ear);		        /* Remove the attach message */
	xpurgeQ((struct Q FAR *)&xug->g_ear);	/* Free the detach message */
	xug->gstate = G_OPEN;
	xug->pvcflg=0;
	((struct pvcattf FAR *) mp->b_rptr)->result_code = PVC_USRERROR;
#if ENDIAN_SWAP
	x25apiswap(mp, UPWARD) ;
#endif
	qreply(xug->wq,mp);
    }
}


/************************************************************************/
void make_pvcdbs(cfg)
/************************************************************************/

/*
    Allocates the PVC PLP db elements. Prints warning if insufficient
    elements.
*/

struct wlcfg *cfg;

{
    register struct x25vcformat FAR *xvc;
    int i, j, ref;
    short lcival;

    lcival = (short) cfg->LPC;

    for(i=0; i<cfg->NPCchannels; i++)
    {
	if( (xvc=x25allocvc(&ref)) )
	{
	    xvc->xbase = cfg;			/* Set up the config ptr */

	    /*
		Set up default parameters for the PVC to run unattached
	    */
	    xvc->rpktp = (unsigned char) cfg->REMDEFPKTSIZE;
	    xvc->lpktp = (unsigned char) cfg->LOCDEFPKTSIZE;
	    xvc->rpkt  = ( 1 << cfg->REMDEFPKTSIZE );
	    xvc->lpkt  = ( 1 << cfg->LOCDEFPKTSIZE );
	    xvc->rwsize= (unsigned char) cfg->REMDEFWSIZE;
	    xvc->lwsize= (unsigned char) cfg->LOCDEFWSIZE;
	    xvc->CCITTflag |= EXPINUSE;		/* Expedited allowed */

	    xvc->ampvc = 1;			/* Mark as a PVC	*/
	    xvc->xtag = X25TAG;
	    xvc->nsref = ref;
	    xvc->xstate = DataTransfer;		/* PVC startup state	*/
	    xvc->xu_ident = cfg->U_SN_ID;
	    xvc->nslim = 0;
	    xvc->xdomain = net_domain(cfg);
	    xvc->lci = lcival;			/* LCI's sequential	*/
#ifdef MOD8ONLY
	    xvc->lgngfi = ((lcival >> 8 ) & 255 ) | G_8;
	    xvc->xmodulo = 7;
#else
	    xvc->lgngfi = ((lcival >> 8 ) & 255 ) | cfg->THISGFI;

	    if( cfg->THISGFI == G_8 )
		xvc->xmodulo = 7;
	    else
		xvc->xmodulo = 127;
#endif
	    xvc->lcn = (lcival&255);
	    (void) vc_in_range(lcival,&ref,cfg,&j);/* NB PVCS enabled */
	    xvc->hashval = (unsigned short)ref;	/* Get hash for setup */

	    lcival++;				/* The next PVC LCI	*/
	}
	else
	{
	    strlog(X25_STID,0,0,SL_ERROR,"X25: insufficient dbs for PVC channels");
	    break;
	}
    }
}


/************************************************************************/
void rmPVCusr(xug)
/************************************************************************/

/*
    Detaches the glue from the X.25 db structure and sets event to enact
    in PLP. New PVC user cannot attach until this is complete.

*/

register struct xgldbf *xug;

{
    struct x25vcformat FAR *xvc;

    /*
	Sanity checks first
    */
    if( (xug->xnetref>=FIRSTVC) && (xug->xnetref<=isovcmax) )
    {
	xvc=(struct x25vcformat FAR *) iso_vc[xug->xnetref];
	if( xvc->xtag==X25TAG && xvc->NSUattached!=0 && xvc->ampvc != 0 )
	{
	    xvc->NSUattached = 0;
	    xvc->nsucause = 0;
	    xvc->nsudiag = 0;
	    xvc->xusrevent |= xDRout;		/* Means 'usr detach' */
	    x25vccall(xvc);			/* Enact the detach */
	}
    }
}


/************************************************************************/
struct xgldbf *PVC_wtgglue(sn,lc)
/************************************************************************/

/*
    Finds a waiting glue db for a PVC. Returns pointer to it or NULL if
    not found.
*/

unsigned long  sn;
unsigned short lc;

{
    int m;
    struct pvcattf FAR *pb;

    for(m=0; m<x25devmax; m++)
    {
	if( xgldb[m].gstate  == G_PVCWAITING )
	{
	    pb=(struct pvcattf FAR *) (xgldb[m].g_ear.head->b_rptr);
	    
	    if( pb->sn_id == sn && pb->lci   == lc )
		return(&xgldb[m]);
	}
    }
    return(NULL);			/* Not found */
}


/************************************************************************/
struct x25vcformat FAR *PVC_db(sn,lc)
/************************************************************************/

/*
    Finds PVC vc database element.  Returns pointer to it or NULL if
    not found.
*/

unsigned long sn;
unsigned short lc;

{
    struct x25vcformat FAR *xvc;
    int j;

    for(j=FIRSTVC; j<=isovcmax; j++)
    {
	xvc = (struct x25vcformat FAR *) iso_vc[j];

	if( xvc->xtag == X25TAG && xvc->ampvc!= 0 &&
	    xvc->xu_ident == sn && xvc->lci == lc )
	{
	    return(xvc);
	}
    }
    return((struct x25vcformat FAR *)NULL);		/* None found */
}


/************************************************************************/
int chk_PVC(cfg,pa)
/************************************************************************/

/*
    Checks PVC supplied parameters.  Zero returned if acceptable.
    Sets defaults too.
*/

struct wlcfg	*cfg;
struct pvcconff	FAR *pa;

{
    unsigned char par;

    /*
	Check the packet and window parameters specified
    */
    if( pa->rempacket != 0 )
    {
	if( pa->rempacket < 4 || pa->rempacket >12 )
	    return(-1);
    }
    else
	pa->rempacket = cfg->REMDEFPKTSIZE;
	
    if( pa->locpacket != 0 )
    {
	if( pa->locpacket < 4 || pa->locpacket >12 )
	    return(-1);
    }
    else
	pa->locpacket = cfg->LOCDEFPKTSIZE;

#ifdef MOD8ONLY
    par = 7;
#else
    par = (cfg->THISGFI == G_8 ? 7 : 127 );
#endif

    if( pa->remwsize != 0 )
    {
	if( pa->remwsize > par )
	    return(-1);
    }
    else
	pa->remwsize = cfg->REMDEFWSIZE;

    if( pa->locwsize != 0 )
    {
	if( pa->locwsize > par )
	    return(-1);
    }
    else
	pa->locwsize = cfg->LOCDEFWSIZE;

    return(0);			/* OK, acceptable */
}


/************************************************************************/
void join_pvc_to_glue(xvc,xug,pa)
/************************************************************************/

/*
    Performs the attach functions for PVC and glue.  Supplied parameters
    checked OK before this call.
*/

struct x25vcformat	FAR *xvc;
struct xgldbf		*xug;
struct pvcattf		FAR *pa;

{
    /*
	Load the values into the vc database
    */
    xvc->rem_Dsupport = (unsigned char) pa->reqackservice;
    xvc->loc_Dsupport = (unsigned char) pa->reqackservice;	

    if( pa->reqnsdulimit )
	xvc->nslim = (ushort) pa->nsdulimit;
    else
	xvc->nslim = (unsigned char) xvc->xbase->MAXNSDULEN;

    /*
	Attach the user
    */
    xvc->NSUattached = 1;
    xvc->userref = xug->gref;
    xug->xnetref = xvc->nsref;
    xvc->est_time = curtime();	/* SNMP: Establish time */
    xvc->direction = X25_DRCTN_PVC; /* SNMP: Establish direction */
}


/************************************************************************/
int att_PVC(xug, mp)
/************************************************************************/

/*
    Starts the attach procedure for a pvc. Returns PVC_SUCCESS if OK,
    PVC_WAIT if OK but wait on state change, otherwise an error message
    (see x25_proto.h).  The buffer is left with the glue for attach response.
*/

register struct xgldbf *xug;
register mblk_t *mp;

{
    int i;
    struct x25vcformat FAR *xvc;
    struct pvcattf     FAR *pa;
    struct wlcfg       *cfg;

    pa = (struct pvcattf FAR *) mp->b_rptr;
    
    for(i=0; i<x25cfgmax; i++)		/* Find config base for this PVC */
    {

	if( xcfg[i].net_ok != 0 && xcfg[i].net_cfg.U_SN_ID == pa->sn_id )
	{
	    cfg = (&xcfg[i].net_cfg);	/* Check the lci specified */
	    
	    if( cfg->NPCchannels != 0 && (unsigned short)cfg->LPC <= pa->lci &&
	                                 (unsigned short)cfg->HPC >= pa->lci )
	    {
		/*
		    Find the PLP database element for this lci and net
		*/
		if( xvc = PVC_db(pa->sn_id, pa->lci) )
		{
		    /*
			Found one.  Check that the state is OK for a
			response and no other glue is waiting on this PVC.
		    */
		    if( xvc->NSUattached == 0 && PVC_wtgglue(pa->sn_id, pa->lci)==NULL )
		    {
			/*
			    Attach or wait on it
			*/
			if( xvc->xstate == DataTransfer &&
			    xvc->xusrevent == 0         &&
			    xvc->lsaplink               &&
			    xvc->lsaplink->lstate == L_connected )
			{
			    /*
				Can proceed with the PVC - set up pars
			    */
			    join_pvc_to_glue(xvc,xug,pa);
			    map_dbit_control(xvc);
			    return(PVC_SUCCESS);
			    /*
				If an enable PVC event implemented, put here
			    */
			}
			else			/* Await state? */
			{
			    return(PVC_WAIT);
			}
		    }
		    else
			return(PVC_BUSY);
		}
		else
		    return(PVC_NODBELEMENTS);
	    }
	    else
		{

			printf("NPCchannels = %x cfg->LPC = %x cfg->HPC = %x pa->lci =%x",cfg->NPCchannels, cfg->LPC, cfg->HPC, pa->lci) ;
			return(PVC_CFGERROR);

		}

	}
    }
    return(PVC_NOSUCHSUBNET);		/* Dropped out of config search */
}



/************************************************************************/
void hunt_PVC_user(xvc)
/************************************************************************/

/*
    Based on the lci etc. a search is made for a waiting PVC user
    and if found attach is made.
*/

struct x25vcformat FAR *xvc;

{
    struct xgldbf	*xug;
    mblk_t		*mp;

    if( xug = PVC_wtgglue(xvc->xu_ident,xvc->lci) )
    {
	xug->gstate = G_ATTACHED;
	mp=OFF(&xug->g_ear);
	((struct pvcattf FAR *)mp->b_rptr)->result_code = PVC_SUCCESS;

	join_pvc_to_glue(xvc,xug,((struct pvcattf FAR *)mp->b_rptr));
#if ENDIAN_SWAP
	x25apiswap(mp, UPWARD) ;
#endif
	putnext(xug->rq,mp);
    }

}



/************************************************************************/
int close_x25_tracestrm(q)
/************************************************************************/

/*
    Deactivates tracing on any subnetwork whose tracestream matches that
    which is closing down. Returns number of subnetworks processed.
*/

queue_t  *q;

{
    int i;
    int snid_found = 0;
			
    for( i = 0 ; i < x25cfgmax ; i++ )
    {
	if( xcfg[i].net_ok != 0 &&
	    xcfg[i].trace_active != 0 )
	{
	    if ( xcfg[i].trace_queue == q )
	    {
		snid_found++;
		xcfg[i].trace_active = 0;
		xcfg[i].trace_queue  = 0;
	    }
	}
    }
    return(snid_found);
}


