#ident "$Id: x25_stream.c,v 1.10 1997/10/21 20:48:48 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
 *
 *  X25_STREAM.C
 *
 *
 *******************************************************************/

/*
 *	x25_stream.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25_stream.c
 *	@(#)x25_stream.c	1.69
 *
 *	Last delta created	17:17:44 11/18/92
 *	This file extracted	18:32:48 11/19/92
 *
 */

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

#define BUFP_T bufp_t

#define	STRMUSER	u.u_uid

/* #include "sysmacros.h" */

#include "x25_inc.h"

#include "errno.h"

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

#ifndef NULL
#define NULL 0
#endif


queue_t *SNMP_trace_q = (queue_t *) 0 ;

extern struct user u;
extern mblk_t *allocb(), *getq(), *copymsg();

/* ------ The STREAMS structures ------ */

static int x25uopen(), x25uclose(), x25ursrv(), x25uwsrv(), x25uwput();
static int x25lrput(), x25lrsrv(), x25lwsrv();
int x25lwput();


static struct module_info winfo = {
      X25_STID, "x25", 0, INFPSZ, 8192, 2048 
};

static struct module_info rinfo = {
      0, "x25", 0, INFPSZ, 8192, 2048
};
/*
static struct module_info winfo = {
      X25_STID, "x25", 0, INFPSZ, 512, 128
};

static struct module_info rinfo = {
      0, "x25", 0, INFPSZ, 512, 128
};
*/
/*
static struct module_info rinfo = {
      0, "x25", 0, INFPSZ, 0x8000, 0x4000
};
*/

static struct qinit urinit = {			/* Upper read */
          NULL, x25ursrv, x25uopen, x25uclose, NULL, &rinfo, NULL
};

static struct qinit uwinit = {			/* Upper write */
      x25uwput, x25uwsrv,     NULL,      NULL, NULL, &winfo, NULL
};

static struct qinit lrinit = {			/* Lower read */
      x25lrput, x25lrsrv,     NULL,      NULL, NULL, &rinfo, NULL
};

static struct qinit lwinit = {			/* Lower write */
      x25lwput, x25lwsrv,     NULL,      NULL, NULL, &winfo, NULL
};

struct streamtab x25info = {&urinit, &uwinit, &lrinit, &lwinit };




/* ------ Internal states for obtaining listen entries ------- */

#define		X_cumode	0
#define		X_culength	1
#define		X_cubytes	2
#define		X_lmode		3
#define		X_ltype		4
#define		X_llength	5
#define		X_ladd		6

/* ------ Externally kept pointer to the controlling stream ------ */

extern queue_t *x_ctlstrm;

/* ------ Statistics structure ------ */

extern int L3monarray[] ;

/* ------ The Glue Database External Declaration for this module ------ */

extern struct xgldbf xgldb[];
#ifndef SOH
extern mblk_t *OFF(), *mb_frag();
#endif
extern struct x25vcformat FAR *x25allocvc(int *);
extern struct xsndbf *nxt_subnet();

extern struct x25vcformat FAR *PVC_db();
mblk_t *find_connid(), *scan_ears();
extern int chk_PVC();

/******************************************************************/
static int x25uopen(q, dev, flag, sflag)
/******************************************************************/
queue_t  *q;
dev_t    dev;
int      flag, sflag;
{
    struct xgldbf *xdev;
static char first_open = TRUE;

#if 0

		printf("X25: Open ") ;
#endif 


    if (x_ctlstrm == NULL && STRMUSER != 0)
	{
		printf("X25: Open fail 1") ; 
		return(OPENFAIL);
	}

    if( sflag == CLONEOPEN )
    {
	for( dev=0; dev< x25devmax; dev++ )
	    if( xgldb[dev].gstate == G_IDLE )
		break;			/* Free record */
    }
    else
	dev = minor(dev);			/* Get minor dev */

    if ((dev >= x25devmax) || (sflag == MODOPEN))	/* Dev number OK? */
    {
	u.u_error = ENXIO;
	printf("X25: Open Fail 2") ;
	return(OPENFAIL);
    }

    /* Q_QPTR CHECK FOR DEVICE ALREADY OPEN */

    if( q->q_ptr != NULL )
	{

		printf("X25: Open 1: Dev = %x",dev) ;
		return(dev);
	}
if( first_open )
{
        x25init();
        first_open = FALSE;
}


    xdev = (&xgldb[dev]);
    xdev->gstate = G_OPEN;		/* Record now open */
    xdev->xuid   = STRMUSER;		/* Retain the effective user id */
    xdev->rq = q;
    xdev->wq = WR(q);			/* Open passes read queue */
    q->q_ptr =        (char *)xdev;	/* Point at the record */
    xdev->wq->q_ptr = (char *)xdev;	/* Point at the record */
    noenable(q);			/* Disable read scheduling */

	printf("X25: Open 2: dev = %x",dev ) ;



    return( dev );			/* Return success */
}


/******************************************************************/
static void xfreeglue(xug)
/******************************************************************/

/*
    Clears out the glue record.
*/

struct xgldbf *xug;
{
    int myref;

    xpurgeQ((struct Q FAR *)&xug->gdbq);	/* Clear out Qs */
    xpurgeQ((struct Q FAR *)&xug->g_ear);
    
    purge_listens(xug->gref);			/* Clear listens  */
    myref = xug->gref;				/* Hold reference */
    bzero((BUFP_T)xug,sizeof( struct xgldbf ));	/* G_IDLE == 0 too */
    xug->gref = myref;				/* Reset reference */
}


/******************************************************************/
static int x25uclose(q,flag)
/******************************************************************/

/*
    X.25 mux close routine
*/

queue_t *q;					/* Read pointer */
int flag;
{
    int i;
    struct xgldbf *xug;
    struct x25vcformat FAR *xvc;

    xug = (struct xgldbf *)q->q_ptr;		/* Point at record (if any) */
    q->q_ptr = WR(q)->q_ptr = NULL;		/* Clear out xug pointer */
    flushq(   q , FLUSHALL);			/* Clear read side */
    flushq(WR(q), FLUSHALL);			/* Clear write side */

    /*
	IF CLEARING THE CONTROL STREAM ZAP ALL RESOURCES
    */
    if( x_ctlstrm == WR(q) )
    {
	xfreeglue(xug);				/* Clear control glue db */
	x_ctlstrm = NULL;

	for(i=0; i<iMAXXDEVS; i++)			/* Free glue resources */
	{
	    xug = (&xgldb[i]);
	    if( xug->gstate != G_IDLE )		/* ERROR to all open */
	    {
		flushq(xug->wq, FLUSHALL);		/* Clear read side */
		flushq(xug->rq, FLUSHALL);		/* Clear write side */
		(void) putctl1(xug->rq->q_next, M_ERROR, ENXIO);
		xug->rq->q_ptr = xug->wq->q_ptr = NULL;
		xfreeglue(xug);
	    }
	}

	for(i=1; i<=iMAXXVCS; i++)		/* Free db resources */
	{
	    xvc = iso_vc[i];
	    if( xvc->xtag == X25TAG )
	    {
		xpurgeQ((struct Q FAR *)&xvc->x25txqhead);
		m_freem(xvc->xEDbuff);
		m_freem(xvc->datatogo);
		m_freem(xvc->reassemble);
		m_freem(xvc->Udiscdata);
		m_freem(xvc->calldata);
	    }
	}

	x25init();					/* Zero out the dbs */
	xug = NULL;					/* Avoid code below */
    }

    if( xug )
    {
	if( xug->gstate != G_OPEN )		/* I.e. something going */
	{				/* or is G_FROZEN  */
	    /*
		If record is attached, disconnect.  If listening and no
		disconnect is waiting, then disconnect also.
	    */

	    if( xug->pvcflg == 0 && xug->gstate == G_ATTACHED )/* Disconnect */
	    {
		if( N_disreq(xug->xnetref,
	    	     	     0x00,
			     0xF2,		/* Abnormal */
			     NULL,
			     (struct xaddrf FAR *)NULL,
			     (struct xaddrf FAR *)NULL,
			     (mblk_t *)NULL) != 0 )	/* Sys error actually */
		{
		    strlog(X25_STID,0,0,SL_ERROR,"X25: close DI ref match");
		}
#ifndef INTERNAL_DISC
		else
	        {
		    /*
			Stream closing so no user to send N_DC to
		    */
		    iso_vc[xug->xnetref]->NSUattached=0;
		}
#endif
	    }
	    else if( xug->pvcflg != 0 && xug->gstate == G_ATTACHED )/* rm */
	    {
		rmPVCusr(xug);
	    }
	    else if( xug->gstate == G_LISTENING )
	    {
		/* Progressively remove CI & DIs awaiting processing */
		rmCIDI(xug);
	    }
	}

	/*
	    Now clear out the record
	*/
	xfreeglue(xug);

	/*
	    If this is a trace stream deactivate tracing on any
	    required subnetworks
	*/
	close_x25_tracestrm(q);
    }

    /*
     * Deactivate SNMP trace stream
     */
    if (SNMP_trace_q == q)
	SNMP_trace_q = (queue_t *) 0 ;

	return 1;

}


/******************************************************************/
static int x25uwput(q, mp)
/******************************************************************/

/*
    X.25 mux upper write put routine.
*/

queue_t *q;
mblk_t  *mp;

{
    mblk_t  *temp, *templ, *mt;
    unsigned char cmnd;
    int i, j;
    S_X25_HDR *hdr ;

    NUI_ADDR	*thisnui;

#if 0
		printf("X25: Message Type = %x",mp->b_datap->db_type) ;
#endif

    switch(mp->b_datap->db_type)
    {

    case M_PCPROTO:

	hdr = (S_X25_HDR *) mp->b_rptr ;
#if ENDIAN_SWAP
	SWAP32(hdr->xl_type) ;
	SWAP32(hdr->xl_command) ;
#endif
	/*
	    Only priority listens supported. Fall through to PROTO case if
	    OK else	discard.
	*/
	if ( (hdr->xl_type    != XL_CTL) &&
	     (hdr->xl_command != N_Xlisten) )
	{
	    L3monitor(xiffnerror,0);
	    freemsg(mp);
	    break;
	}
#if ENDIAN_SWAP
	/* Undo swap because M_PROTO will do it again */
	SWAP32(hdr->xl_type) ;
	SWAP32(hdr->xl_command) ;
#endif
    
    case M_PROTO:			/* The most common            */

	if( pr_length(mp) < 2 )		/* Sanity - user unreliable   */
	{
	    freemsg(mp);		/* Discard (could be M_ERROR) */
	    return 1;			/* To await further events    */
	}

#ifdef DEBUG
	if(mp->b_cont)
	{		
		printf("payload = %x",*mp->b_cont->b_rptr ) ;
	}
#endif

#if ENDIAN_SWAP
	x25apiswap(mp, DOWNWARD) ;
#endif
	hdr = (S_X25_HDR *) mp->b_rptr ;

	cmnd = (unsigned char) hdr->xl_command ;
	
	switch( hdr->xl_type )
	{

	case XL_DAT:			/* Q data for scheduler */

	    if( cmnd == N_Data )
		putq(q,mp);			/* May or may not schedule */
	    else
	    {
		if( cmnd == N_EData || cmnd == N_EAck || cmnd == N_DAck )
		{
		    if( q->q_first == NULL )
			putq(q,mp);	/* Should schedule q but qenable */
					/* called anyway (below) */
					
		    else if( ((struct xdataf FAR *)q->q_first->b_rptr)->xl_command == N_Data )
		    {
			insq(q,q->q_first,mp);
		    }				/* Enabled below */
		    else
		    {
			/* Search through for the last non-data message */
			templ= NULL;
			temp = q->q_last;
			if( ((struct xdataf FAR *)temp->b_rptr)->xl_command != N_Data )
			{
			    putq(q,mp);		/* Just queue message */
			}			/* Enabled below */
			else
			{
			    while(1)
			    {
				if( temp != NULL &&
				    ((struct xdataf FAR *)temp->b_rptr)->xl_command == N_Data )
				{
				    templ = temp;
				    temp  = temp->b_prev;
				}
				else
				    break;	/* templ is last N_Data */
			    }
			    insq(q,templ,mp);	/* Enabled below */
			}
		    }
		    qenable(q);			/* For all expedited cases */
		}
		else
		{
		    /*
			xl_command not recognised.  Wait till scheduled to
			determine action vis-a-vis the interface state
		    */
		    putq(q,mp);
		    qenable(q);			/* And enable the queue */
		}
	    }

	break;


	case XL_CTL:
#ifdef DEBUG
		if(mp->b_cont)
		{
			printf("payload = %x",*mp->b_cont->b_rptr ) ;
		}
#endif
	    if( cmnd == N_DI || cmnd == N_RI || cmnd == N_RC ||
	        cmnd == N_PVC_DETACH )
	    {
		rmXLDAT(q);
	    }
	    putq(q,mp);			/* Q for scheduler */
	    qenable(q);
	    
	break;


	default:

	    L3monitor(xiffnerror,0);
	    freemsg(mp);
	    
	break;

	}
	
    break;


    case M_FLUSH:				/* Canonical flush processing */

	if(*mp->b_rptr & FLUSHW)
	    flushq(q,FLUSHDATA);

	if(*mp->b_rptr & FLUSHR)
	{
	    flushq(RD(q),FLUSHDATA);
	    *mp->b_rptr &= ~FLUSHW;
	    qreply(q,mp);
	}
	else
	    freemsg(mp);

    break;


    case M_IOCTL:				/* IO control commands */
    {				/* These are 'trusted' */
	struct  iocblk FAR *iocp;
	struct linkblk FAR *linkp;
	struct xsndbf  *xsn;
	mblk_t *replybuf;
	struct Ngstat *statp;
	struct Nzstat *zstatp;
	int *L3ptr ;

	iocp = (struct iocblk FAR *) mp->b_rptr;
#ifdef DEBUG
	printf("X25: Ioctl = %x",iocp->ioc_cmd) ;
#endif

	/*  Check the privilege  */
	if ( (iocp->ioc_uid != 0) && (iocp->ioc_cmd != N_getstats) )
	{
	    printf("iocp->ioc_uid = %d",(iocp->ioc_uid)) ; 
	    printf("iocp->ioc_cmd = %d",(iocp->ioc_cmd)) ; 
	    iocp->ioc_error = EACCES;
	    goto iocnak;
	}

	switch(iocp->ioc_cmd )
	{

	case I_LINK:			/* Link up a subnetwork to the mux */

	    linkp = (struct linkblk FAR *)mp->b_cont->b_rptr;
	    if( (xsn = nxt_subnet()) != NULL )
	    {
		xsn->linked = 1;
		xsn->lindex = linkp->l_index;
		xsn->wq     = linkp->l_qbot;
		xsn->rq     = RD(xsn->wq);
		xsn->wq->q_ptr = xsn->rq->q_ptr = (char *) xsn;
		noenable(xsn->wq);	/* Disable from scheduling */
	    }
	    else
		goto iocnak;		/* No room, fail the ioctl */

					/* OK this LINK ioctl */
	    goto iocack;

	case I_UNLINK:

	    linkp = (struct linkblk FAR *)mp->b_cont->b_rptr;
	    for( i=0; i<x25submax; i++)
	    {			/* Find the subnetwork using l_index */
		if( xsndb[i].lindex == linkp->l_index ||
		    (linkp->l_index == (-1) && xsndb[i].linked != 0 ) )
		{
		    xsn = (&xsndb[i]);
		    flushq(xsn->wq, FLUSHALL);
		    flushq(xsn->rq, FLUSHALL);
		    xsn->wq->q_ptr = NULL;
		    xsn->rq->q_ptr = NULL;
		    bzero((BUFP_T)xsn,sizeof( struct xsndbf ));
		    xsn->linkref = i;	/* Replace the link reference */

		    /* The zero clears out the registered OK flag and
		       so on.  Now need to abort all link connections 
		       on this subnetwork - i is the 'hard index' */

		    kill_subnet(i);	/* Successively calls L2_disind */
		    goto iocack;	/* Successfully unlinked */
		}
	    }

	    goto iocack;		/* NEVER Fail UNLINK (11.15 SPG) */

	case N_snident:			/* Registration for subnetwork */

	    /*
		Restrict subnetwork registrations to the control stream.
	    */

	    if (x_ctlstrm ==  NULL)
	    {
		x_ctlstrm = q;
		x25clock((caddr_t) 0);
	    }
	    else if (x_ctlstrm != q)
		goto iocnak;

	    if( iocp->ioc_count == sizeof( struct xll_reg ) &&
	        mb_bulen(mp)    == sizeof( struct xll_reg ) )
	    {
		struct xll_reg reg;

		getNbytes(mp,sizeof( struct xll_reg ),(unsigned char *) &reg);
#if ENDIAN_SWAP
		SWAP_RANGE(&reg, &reg.lreg.ll_snid) ;
		SWAP_RANGE(&reg.lreg.ll_normalSAP, &reg.lmuxid) ;
#endif
		for( i=0; i<x25submax; i++)
		{
		    if( xsndb[i].linked != 0          &&
			xsndb[i].lindex == reg.lmuxid &&
			xsndb[i].regok  == 0 )
		    {
			if( (mt = allocb(sizeof( struct ll_reg ),BPRI_MED)) != NULL )
			{
			    mt->b_datap->db_type = M_PROTO;
			    mt->b_wptr = mt->b_rptr + sizeof(struct ll_reg);
			    bcopy((BUFP_T)&reg.lreg,
				  (BUFP_T)mt->b_rptr,sizeof(struct ll_reg));
			    ((struct ll_reg FAR *)mt->b_rptr)->ll_type = LL_REG;
			    xsndb[i].u_snid = reg.lreg.ll_snid;
/*
			Shouldn't this all be swapped???
*/

			    putnext(xsndb[i].wq,mt);
			    goto iocack;
			}
			else
			    goto iocnak;
		    }
		}
		if( i == x25submax )
		    goto iocnak;
	    }
	    else{

			printf("N_snident Failed. iocp->ioc_count = %x,  sizeof(ll_reg) = %x, mp_bulen = %x", iocp->ioc_count, sizeof(struct ll_reg), mb_bulen(mp)) ; 

			goto iocnak;
		}
	break;

	case N_snmode:			/* Alter subnetwork mode map */

	    if( iocp->ioc_count == sizeof( struct snoptformat ) &&
		mb_bulen(mp)    == sizeof( struct snoptformat ) )
	    {
		struct snoptformat sntemp;

		getNbytes(mp,sizeof(struct snoptformat),(unsigned char *) &sntemp);
#if ENDIAN_SWAP
		SWAP_THIS(&sntemp, sizeof(struct snoptformat)) ;
#endif
		for(i=0; i<x25cfgmax; i++)
		{
		    if( xcfg[i].net_ok != 0 &&
			xcfg[i].net_cfg.U_SN_ID == sntemp.U_SN_ID )
			break;
		}

		if( i<x25cfgmax )
		{
		    if( sntemp.rd_wr != 0 )
			xcfg[i].net_cfg.SUB_MODES = sntemp.newSUB_MODES;
		    else
		    {
			sntemp.newSUB_MODES = xcfg[i].net_cfg.SUB_MODES;
#if ENDIAN_SWAP
			SWAP_THIS(&sntemp, sizeof(struct snoptformat)) ;
#endif
			putNbytes(mp,sizeof(struct snoptformat),(unsigned char *) &sntemp);
		    }
		    mp->b_datap->db_type = M_IOCACK;
		    qreply(q,mp);	/* ioc_count == as sent down */
		}
		else
		    goto iocnak;
	    }
	    else
		goto iocnak;		/* SNmode failure */
		
	break;			/* End processing N_snmode */
	    

	case N_snconfig:		/* Configure a subnetwork */

        printf ("size 0f wfclg = %d %x\n",sizeof( struct wlcfg ));
	    if( iocp->ioc_count == sizeof( struct wlcfg ) &&
		mb_bulen(mp)    == sizeof( struct wlcfg ) )
	    {
		struct wlcfg wtemp;

		getNbytes(mp,sizeof(struct wlcfg),(unsigned char *) &wtemp);
#if ENDIAN_SWAP
		SWAP_RANGE(&wtemp, &wtemp.psdn_local.thclass_type) ;
		SWAP_RANGE(&wtemp.local_address, &wtemp.local_address.lsap_len) ;
#endif
		printf("N_snconfig = %x",wtemp.U_SN_ID) ;

		for(i=0; i<x25cfgmax; i++)
		{
		    if( xcfg[i].net_ok != 0 &&
			xcfg[i].net_cfg.U_SN_ID == wtemp.U_SN_ID )
			break;
		}
		if( i<x25cfgmax )	/* Re-configuration */
		{
			printf("N_snconfig re-config U_SN_ID = %x", wtemp.U_SN_ID) ;
		    bcopy((BUFP_T)&wtemp,(BUFP_T)&xcfg[i].net_cfg,sizeof(struct wlcfg));
		    goto iocack;
		}
		else
		{
		    for(i=0; i<x25cfgmax; i++)
			if( xcfg[i].net_ok == 0 )
			    break;
		    if( i<x25cfgmax )	/* New configuration */
		    {
			printf("N_snconfig U_SN_ID=%x i=%x x25cfgmax=%x, &xcfg[i]=%x",wtemp.U_SN_ID, i, x25cfgmax, &xcfg[i]) ; 
			xcfg[i].net_ok=1;
			bcopy((BUFP_T)&wtemp,
			      (BUFP_T)&xcfg[i].net_cfg,sizeof(struct wlcfg));
			/* Set up this networks database elements */
			make_pvcdbs(&xcfg[i].net_cfg);
			goto iocack;
		    }
		    else
			goto iocnak;
		}
	    }
	    else
		{
			printf("N_snconfig reject") ;		/* Configure a subnetwork */
		goto iocnak;		/* Config failure */
		}

	case N_getstats:		/* Return the current stats */

	    if (x_ctlstrm == NULL || !mp->b_cont)
		goto iocnak;

	    if(mp->b_cont->b_wptr - mp->b_cont->b_rptr < sizeof(struct Ngstat))
		goto iocnak;	/* Buffer not big enough */

	    /*  Allocate a buffer if required	*/

	    statp = (struct Ngstat *) mp->b_cont->b_rptr ;
#if ENDIAN_SWAP
	    SWAP32(statp->snid) ;
#endif
	    if ((i = map_snident(statp->snid)) == (unsigned long)-1)
		goto iocnak;

	    L3ptr = xsndb[i].statptr ;

	    bcopy((BUFP_T)L3ptr, (BUFP_T)&statp->starray, MONARRAYSIZE);
#if ENDIAN_SWAP
	    SWAP_THIS(statp, sizeof(struct Ngstat)) ;
#endif
	    iocp->ioc_count = MONARRAYSIZE ;
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q,mp);	/* ioc_count as set above */

	break;			/* End processing N_getstats */


	case N_zerostats:		/* Reset the current stats */

	    if (x_ctlstrm == NULL)
		goto iocnak;

	    if(mp->b_cont->b_wptr - mp->b_cont->b_rptr < sizeof(struct Nzstat))
		goto iocnak;	/* Buffer not big enough */

	    zstatp = (struct Nzstat *) mp->b_cont->b_rptr ;
#if ENDIAN_SWAP
	    SWAP32(zstatp->snid) ;
#endif
	    if ((i = map_snident(zstatp->snid)) == (unsigned long)-1)
		goto iocnak;

	    L3ptr = xsndb[i].statptr ;

	    bzero((BUFP_T)L3ptr, MONARRAYSIZE);
	    iocp->ioc_count = 0;
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q,mp);	/* ioc_count as set above */
	    
	break;			/* End processing N_zerostats */


	case N_snread:			/* Read subnetwork config */
	{
	    struct wlcfg FAR *config = (struct wlcfg FAR *)mp->b_cont->b_rptr;

	    /*
		If anyone ever sees an implementation where we *don't
		get the ioctl arg in one M_DATA, please feel free to play
		with pullupmsg() here...
	    */
	    if( iocp->ioc_count != sizeof(struct wlcfg) )
	        goto iocnak;

#if ENDIAN_SWAP
	    SWAP32(config->U_SN_ID) ;
#endif
	    for(i=0; i<x25cfgmax; i++)
	    {
		if( xcfg[i].net_ok != 0 &&
		    xcfg[i].net_cfg.U_SN_ID == config->U_SN_ID )
		{
		    break;
		}
	    }

	    if( i>=x25cfgmax )	/* Subnetwork id known */
		goto iocnak;	/* No such identifier known */

	    bcopy((BUFP_T)&xcfg[i].net_cfg,
		  (BUFP_T)config,sizeof(struct wlcfg));

#if ENDIAN_SWAP
	    SWAP_RANGE(config, &config->psdn_local.thclass_type) ;
	    SWAP_RANGE(&config->local_address, &config->local_address.lsap_len) ;
#endif
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q,mp);	/* ioc_count as set above */
	    
	}
	break;


	case N_putpvcmap:		/* Alter P/W sizes of PVCs */
	{
	    struct pvcconff FAR *map = (struct pvcconff FAR *)mp->b_cont->b_rptr;
	    struct x25vcformat FAR *xvc;
	    struct wlcfg *cfg;

	    if( iocp->ioc_count != sizeof(struct pvcconff) )
		goto iocnak;

#if ENDIAN_SWAP
	    SWAP_THIS(map, sizeof(struct pvcconff)) ;
#endif
	    /*
	        Find configuration base for specified subnetwork
	    */
	    for(i=0; i<x25cfgmax; i++)
	    {
		if( xcfg[i].net_ok != 0 &&
		    xcfg[i].net_cfg.U_SN_ID == map->sn_id )
		    break;
	    }

	    if( i>=x25cfgmax )	/* Subnetwork id known */
		goto iocnak;	

	    /*
	        Check supplied lci is specified for subnetwork
	    */
	    cfg = (&xcfg[i].net_cfg);

	    if( cfg->NPCchannels == 0 ||
	        map->lci < (unsigned short)cfg->LPC 
			|| map->lci > (unsigned short)cfg->HPC   )
	    {
	        goto iocnak;
	    }

	    /*
	        Check supplied P/W parameters
	    */
	    if ( chk_PVC(cfg,map) != 0 )
	        goto iocnak;
		
	    /*
	        Find the PLP database element for this lci and net
	    */
	    if (xvc = PVC_db(map->sn_id, map->lci))
	    {
		/*
		    Load the values into the vc database
		*/
		xvc->rpktp = (unsigned char) map->rempacket; xvc->rpkt = (1 << xvc->rpktp);
		xvc->lpktp = (unsigned char) map->locpacket; xvc->lpkt = (1 << xvc->lpktp);
		xvc->rwsize= (unsigned char) map->remwsize ;
		xvc->lwsize= (unsigned char) map->locwsize ;

		goto iocack;
	    }
	    else
	        goto iocnak;
	}


	case N_getpvcmap:		/* Read P/W sizes of PVCs */
	{
	    struct pvcmapf  FAR *map = (struct pvcmapf  FAR *)mp->b_cont->b_rptr;
 	    struct pvcconff FAR *cnf = (struct pvcconff FAR *)map->entries;
	    struct x25vcformat FAR *xvc;

	    if( iocp->ioc_count != sizeof(struct pvcmapf) )
		goto iocnak;

#if ENDIAN_SWAP
	    SWAP32(map->first_ent) ;
	    SWAP32(map->num_ent) ;
#endif
	    /*
	        Step through vc database from the given starting
		point looking for pvcs and record their details in
		structure
	    */
	    for(i=map->first_ent ; i<=isovcmax ; i++)
	    {
		xvc = (struct x25vcformat FAR *) iso_vc[i];
		if( xvc->xtag == X25TAG && xvc->ampvc != 0 )
		{
		    if ( map->num_ent < MAX_PVC_ENTS )
		    {
		    	cnf->sn_id     = xvc->xu_ident;
			cnf->lci       = xvc->lci;
			cnf->locpacket = xvc->lpktp;
			cnf->rempacket = xvc->rpktp;
			cnf->locwsize  = xvc->lwsize;
			cnf->remwsize  = xvc->rwsize;

			map->num_ent++;
			cnf++;
		    }
		    else
		    	break;
		}
	    }
	    map->first_ent = i;              /* where to continue search */
#if ENDIAN_SWAP
	    SWAP_THIS(map, sizeof(struct pvcmapf)) ;
#endif
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q,mp);	             /* ioc_count as set above   */
	}
	break;


        case N_nuiput:
	{     
	    struct nui_put FAR *nuipp = 
		              (struct nui_put FAR *) mp->b_cont->b_rptr;
#ifdef DEBUG
	    printf("NUI_PUT ioctl, NUI = %s\n",nuipp->nuid.nui_string);
#endif
	    if( iocp->ioc_count != sizeof(struct nui_put))
		goto iocnak ;

#if ENDIAN_SWAP
	    SWAP_RANGE(nuipp, &nuipp->nuid.nui_len) ;
	    SWAP_THIS(&nuipp->nuifacility, sizeof(struct facformat)) ;
#endif
	    if(!(nuifac_put(nuipp, NUI_PERMANENT)))
	    {
		goto iocnak;
	    }
	    
	    mp->b_datap->db_type = M_IOCACK;
	    iocp->ioc_count = 0;
	    qreply(q, mp);
	}
	break;


        case N_nuidel:
	{
	    struct nui_del FAR *nuidp =
		     	(struct nui_del FAR *) mp->b_cont->b_rptr;
#ifdef DEBUG
	    printf("NUI_DEL ioctl, NUI = %s\n",	nuidp->nuid.nui_string);
#endif
	    if (iocp->ioc_count != sizeof(struct nui_del))
		goto iocnak ;

#if ENDIAN_SWAP
	    SWAP_RANGE(nuidp, &nuidp->nuid.nui_len) ;
#endif
	    if (!nuifac_del((char *) nuidp->nuid.nui_string,(unsigned char) nuidp->nuid.nui_len))
	    {
		goto iocnak;
	    }

	    mp->b_datap->db_type = M_IOCACK;
	    iocp->ioc_count = 0;
	    qreply(q, mp);
	}
	break;


	case N_nuiget:
	{
	    struct nui_get FAR *nuigp =
			(struct nui_get FAR *) mp->b_cont->b_rptr;
#ifdef DEBUG
	    printf("NUI_GET ioctl, NUI = %s\n",nuigp->nuid.nui_string);
#endif
	    if (iocp->ioc_count != sizeof(struct nui_get))
	        goto iocnak;

#if ENDIAN_SWAP
	    SWAP_RANGE(nuigp, &nuigp->nuid.nui_len) ;
	    SWAP_THIS(&nuigp->nuifacility, sizeof(struct facformat)) ;
#endif
	    if ((thisnui =(NUI_ADDR *)nuifac_get(nuigp->nuid.nui_string,nuigp->nuid.nui_len)) == 
		                                            (NUI_ADDR *) NULL)
	    {
		goto iocnak;
	    }

	    /* copy NUI */
	    bcopy((char *)&thisnui->nuid,
	          (char *)&(nuigp)->nuid, sizeof(struct nuiformat));
		  
	    /* copy Facility */
	    bcopy((char *)&thisnui->nuifacility,
	          (char *)&(nuigp)->nuifacility,sizeof(struct facformat));

#if ENDIAN_SWAP
	    SWAP_RANGE(nuigp, &nuigp->nuid.nui_len) ;
	    SWAP_THIS(&nuigp->nuifacility, sizeof(struct facformat)) ;
#endif
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q, mp);
	}
	break;
	

	case N_nuimget:

	    if (iocp->ioc_count != sizeof(struct nui_mget))
	    {
#ifdef DEBUG
		printf("x25_stream: struct not equal size\n");
#endif
		goto iocnak;
	    }

	    if (!nuifac_dump(q, mp))
	    {
#ifdef DEBUG
		printf("x25_stream: Dump failed\n");
#endif
		goto iocnak;
	    }
	    
	break;	

	case N_nuireset:
#ifdef DEBUG
	    printf("NUI_RESET ioctl\n");
#endif
	    if (iocp->ioc_count != sizeof(struct nui_reset))
		goto iocnak;

	    nuifac_init();

	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q, mp);
	    
	break;	

/* End of NUI Table ioctls */

	case N_getnliversion:	/* Request for current NLI version no */
	{
	    struct nliformat *nliversion;

	    if (iocp->ioc_count != sizeof(struct nliformat))
		goto iocnak;

	    nliversion = (struct nliformat FAR *) mp->b_cont->b_rptr;
	    nliversion->version = NLI_VERSION;
#if ENDIAN_SWAP
	    SWAP32(nliversion->version) ;
#endif
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q, mp);
	}
	break;
	    
	case N_traceon:
	case N_traceoff:
	{
	    struct trc_regioc  FAR *reg;
	    int i;
	    int snid_found, snid_busy;

	    reg = (struct trc_regioc  FAR *)mp->b_cont->b_rptr;

	    if ( iocp->ioc_count != sizeof(struct trc_regioc) )
	    {
		iocp->ioc_error = EINVAL;
		goto iocnak;
	    }

#if ENDIAN_SWAP
	    SWAP_THIS(reg, sizeof(struct trc_regioc)) ;
#endif
	    snid_found = snid_busy = 0;
	    
	    if ( iocp->ioc_cmd == N_traceoff )
	    {
		snid_found = close_x25_tracestrm(RD(q));

		if ( snid_found )
		{
		    reg->all_snworks = snid_found;
#if ENDIAN_SWAP
		    SWAP_THIS(reg, sizeof(struct trc_regioc)) ;
#endif
		    iocp->ioc_error  = 0;
		    mp->b_datap->db_type = M_IOCACK;
		    qreply(q,mp);	            /* ioc_count unchanged  */
		    break;
		}
		else
		{
		    iocp->ioc_error = ENODEV;
		    goto iocnak;
		}
	    }
	    else
	    {
		if ( reg->all_snworks )
		{
		    /*
			All subnetworks to trace level specified
		    */
		    for( i = 0 ; i < x25cfgmax ; i++)
		    {
			if( xcfg[i].net_ok != 0 )
			{
			    if ( xcfg[i].trace_active == 0 )
			    {
				xcfg[i].trace_active = (reg->level) ? reg->level : 1;
				xcfg[i].trace_queue  = RD(q);
				reg->active[snid_found++] = xcfg[i].net_cfg.U_SN_ID;
			    }
			    else
			    	snid_busy++;
			}
		    }
		}
		else
		{
		    /*
			Subnetwork and trace level specified.
		    */
		    for(i = 0 ; i < x25cfgmax ; i++)
		    {
			if( xcfg[i].net_ok != 0 &&
			    xcfg[i].net_cfg.U_SN_ID == reg->snid )
			{
			    if ( xcfg[i].trace_active == 0 )
			    {
				xcfg[i].trace_active = (reg->level) ? reg->level : 1;
				xcfg[i].trace_queue  = RD(q);
				reg->active[snid_found++] = xcfg[i].net_cfg.U_SN_ID;

			    }
			    else
				snid_busy = 1;

			    break;
			}
		    }
		}

		if ( snid_found )
		{
		    if ( reg->all_snworks )
		    {
		    	reg->all_snworks      = snid_found;
			reg->active[snid_found] = '\0';
		    }
#if ENDIAN_SWAP
		    SWAP_THIS(reg, sizeof(struct trc_regioc)) ;
#endif
		    iocp->ioc_error = 0;
		    mp->b_datap->db_type = M_IOCACK;
		    qreply(q,mp);	            /* ioc_count unchanged  */
		    break;
		}
		else
		{
		    if ( snid_busy )
		        iocp->ioc_error = EEXIST;
		    else
		        iocp->ioc_error = ENODEV;
		}
	    }
	    goto iocnak;
	}

	case N_getvcstates:		/* Read VC connection states */
	{
	    struct vcinfof  FAR *map = (struct vcinfof  FAR *)mp->b_cont->b_rptr;
 	    struct smallvcinfo FAR *cnf = (struct smallvcinfo FAR *)map->vcinfo;
	    struct x25vcformat FAR *xvc;

            printf("vcinfof size = %d\n",sizeof(struct vcinfof));
	    if( iocp->ioc_count != sizeof(struct vcinfof) )
		goto iocnak;

#if ENDIAN_SWAP
	    SWAP_THIS(map, sizeof(struct vcinfof)) ;
#endif
	    /*
	        Step through vc database from the given starting
		point recording VC state and subnetwork identifier.
	    */
	    for(i=map->first_ent ; i<=isovcmax ; i++)
	    {
		xvc = (struct x25vcformat FAR *) iso_vc[i];
		if( xvc->xtag == X25TAG )
		{
		    if ( map->num_ent < MAX_VC_ENTS )
		    {
		    	cnf->xstate     = xvc->xstate;
		    	cnf->xtag       = xvc->xtag;
			cnf->xu_ident   = xvc->xu_ident;
			cnf->ampvc	= xvc->ampvc;
			map->num_ent++;
			cnf++;
		    }
		    else
		    	break;
		}
	    }
	    map->first_ent = i;              /* where to continue search */
#if ENDIAN_SWAP
	    SWAP_THIS(map, sizeof(struct vcinfof)) ;
#endif
	    mp->b_datap->db_type = M_IOCACK;
	    qreply(q,mp);	             /* ioc_count as set above   */
	}
	break;

	case RFC_1382_ADMN:
	{
		rfc_1382_admn_t *admn ;
		struct wlcfg *cfg ;

		if (!mp->b_cont || mp->b_cont->b_wptr - mp->b_cont->b_rptr !=
			sizeof(rfc_1382_admn_t)) {
			iocp->ioc_error = ENOMEM ;
			goto iocnak ;
		}

		admn = (rfc_1382_admn_t *) mp->b_cont->b_rptr ;
#if ENDIAN_SWAP
		SWAP32(admn->X25AdmnIndex) ;
#endif
		if ((i = map_snident(admn->X25AdmnIndex)) == (unsigned long)-1) {
			iocp->ioc_error = ENODEV ;
			goto iocnak ;
		}

		cfg = &(xcfg[i].net_cfg) ;

		admn->X25AdmnInterfaceMode = ((cfg->L3PLPMODE == 0) ? 2 :
					((cfg->L3PLPMODE == 2) ? 3 : 1)) ;
		admn->X25AdmnMaxActiveCircuits = cfg->Nochnls ;
		admn->X25AdmnPacketSequencing = (cfg->THISGFI == G_128) ? 2 : 1 ;
		admn->X25AdmnRestartTimer = cfg->T20value * 100 ;
		admn->X25AdmnCallTimer = cfg->T21value * 100 ;
		admn->X25AdmnResetTimer = cfg->T22value * 100 ;
		admn->X25AdmnClearTimer = cfg->T23value * 100 ;
		admn->X25AdmnWindowTimer = cfg->Tvalue * 100 ;
		admn->X25AdmnDataRxmtTimer = cfg->T25value * 100 ;
		admn->X25AdmnInterruptTimer = cfg->T26value * 100 ;
		admn->X25AdmnRejectTimer =
		admn->X25AdmnRegistrationRequestTimer = 2147483647 ;
		admn->X25AdmnMinimumRecallTimer = 1 ;
		admn->X25AdmnRestartCount = cfg->R20value ;
		admn->X25AdmnResetCount = cfg->R22value ;
		admn->X25AdmnClearCount = cfg->R23value ;
		admn->X25AdmnDataRxmtCount =
		admn->X25AdmnRejectCount =
		admn->X25AdmnRegistrationRequestCount =
		admn->X25AdmnDefCallParamId = 0 ;
		admn->X25AdmnNumberPVCs = cfg->NPCchannels ;
		bcd_to_str(cfg->local_address.lsap_add,
			cfg->local_address.lsap_len,
			admn->X25AdmnLocalAddress) ;

#if ENDIAN_SWAP
		SWAP_RANGE(admn, &admn->X25AdmnDefCallParamId) ;
#endif
		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	case RFC_1382_STAT:
	{
		rfc_1382_stat_t *stat ;

		if (!mp->b_cont || mp->b_cont->b_wptr - mp->b_cont->b_rptr !=
			sizeof(rfc_1382_stat_t)) {
			iocp->ioc_error = ENOMEM ;
			goto iocnak ;
		}

		stat = (rfc_1382_stat_t *) mp->b_cont->b_rptr ;
#if ENDIAN_SWAP
		SWAP32(stat->X25StatIndex) ;
#endif
		if ((i = map_snident(stat->X25StatIndex)) == (unsigned long)-1) {
			iocp->ioc_error = ENODEV ;
			goto iocnak ;
		}

		L3ptr = xsndb[i].statptr ;

		stat->X25StatInCalls = L3ptr[cll_in] ;
		stat->X25StatInCallRefusals = L3ptr[cll_in] - L3ptr[caa_out] ;
		stat->X25StatInProviderInitiatedClears = L3ptr[pinitclears] ;
		stat->X25StatInRemotelyInitiatedResets = L3ptr[remote_rst] ;
		stat->X25StatInProviderInitiatedResets = L3ptr[provider_rst] ;
		stat->X25StatInRestarts = (L3ptr[res_in] ? (L3ptr[res_in] - 1) : 0) ;
		stat->X25StatInDataPackets = L3ptr[dt_in] ;
		stat->X25StatInAccusedOfProtocolErrors =  L3ptr[prc_errors] ;
		stat->X25StatInInterrupts = L3ptr[ed_in] ;
		stat->X25StatOutCallAttempts = L3ptr[cll_out] ;
		stat->X25StatOutCallFailures = L3ptr[cll_out] - L3ptr[caa_in] ;
		stat->X25StatOutInterrupts = L3ptr[ed_out] ;
		stat->X25StatOutDataPackets = L3ptr[dt_out] ;
		stat->X25StatOutgoingCircuits = L3ptr[caa_in] ;
		stat->X25StatIncomingCircuits = L3ptr[caa_out] ;
		stat->X25StatTwoWayCircuits = L3ptr[caa_in] ;
		stat->X25StatRestartTimeouts = L3ptr[t20_expire] ;
		stat->X25StatRestartTimeouts = L3ptr[t21_expire] ;
		stat->X25StatResetTimeouts = L3ptr[t22_expire] ;
		stat->X25StatClearTimeouts = L3ptr[t23_expire] ;
		stat->X25StatDataRxmtTimeouts = L3ptr[L3T25timeouts] ;
		stat->X25StatInterruptTimeouts = L3ptr[t26_expire] ;
		stat->X25StatRetryCountExceeds = L3ptr[l2r20exp]+L3ptr[r22exp]+L3ptr[r23exp] ;
		stat->X25StatClearCountExceeds = L3ptr[r23exp] ;

#if ENDIAN_SWAP
		SWAP_THIS(stat, sizeof(rfc_1382_stat_t)) ;
#endif
		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	case RFC_1382_CHANNEL:
	{
		rfc_1382_channel_t *channel ;
		struct wlcfg *cfg ;

		if (!mp->b_cont || mp->b_cont->b_wptr - mp->b_cont->b_rptr !=
			sizeof(rfc_1382_channel_t)) {
			iocp->ioc_error = ENOMEM ;
			goto iocnak ;
		}

		channel = (rfc_1382_channel_t *) mp->b_cont->b_rptr ;
#if ENDIAN_SWAP
		SWAP32(channel->X25ChannelIndex) ;
#endif
		if ((i = map_snident(channel->X25ChannelIndex)) == (unsigned long)-1) {
			iocp->ioc_error = ENODEV ;
			goto iocnak ;
		}

		cfg = &(xcfg[i].net_cfg) ;

		channel->X25ChannelLIC = cfg->LIC ;
		channel->X25ChannelHIC = cfg->HIC ;
		channel->X25ChannelLTC = cfg->LTC ;
		channel->X25ChannelHTC = cfg->HTC ;
		channel->X25ChannelLOC = cfg->LOC ;
		channel->X25ChannelHOC = cfg->HOC ;

#if ENDIAN_SWAP
		SWAP_THIS(channel, sizeof(rfc_1382_channel_t)) ;
#endif
		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	case RFC_1382_CIRCUIT:
	{
		rfc_1382_circuit_t *circ ;
		struct x25vcformat *xvc;

		if (!mp->b_cont || mp->b_cont->b_wptr - mp->b_cont->b_rptr !=
			sizeof(rfc_1382_circuit_t)) {
			iocp->ioc_error = ENOMEM ;
			goto iocnak ;
		}

		circ = (rfc_1382_circuit_t *) mp->b_cont->b_rptr ;

#if ENDIAN_SWAP
		SWAP32(circ->X25CircuitIndex) ;
		SWAP32(circ->X25CircuitChannel) ;
#endif
		circ->X25CircuitStatus = 1 ;
		for(i=0 ; i<=isovcmax ; i++) {
			xvc = (struct x25vcformat FAR *) iso_vc[i];
			if (xvc->xtag == X25TAG && xvc->lci == circ->X25CircuitChannel && xvc->xu_ident == circ->X25CircuitIndex) {
				get_xvc_circuit_info(xvc, circ) ;
				break ;
			}
		}
#if ENDIAN_SWAP
		SWAP_RANGE(circ, &circ->X25CircuitCallParamId) ;
#endif
		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	case RFC_1382_TRACE_ON:
	{
		if (SNMP_trace_q) {
			iocp->ioc_error = EACCES ;
			goto iocnak ;
		}

		SNMP_trace_q = RD(q) ;

		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	case RFC_1382_TRACE_OFF:
	{

		SNMP_trace_q = (queue_t *) 0 ;

		mp->b_datap->db_type = M_IOCACK;
		qreply(q, mp);
	}
	break ;

	default:			/* Unknown ioctl */
	
		printf("X25: Unknown Ioctl = %x", iocp->ioc_cmd) ;
	    goto iocnak;

	}				/* End switch on ioc_cmd */

	break;

	/* -------------- FAIL IOCTL EXIT -------------- */

iocnak:

	mp->b_datap->db_type = M_IOCNAK;
	qreply(q,mp);

	break;

	/* -------------- OK IOCTL EXIT -------------- */

iocack:

	mp->b_datap->db_type = M_IOCACK;
	iocp->ioc_count = 0;
	qreply(q,mp);

	break;

	}				/* End M_IOCTL case */

    default:				/* Ignore silently */

	printf("X25: Unkown Message= %x",mp->b_datap->db_type) ;
	freemsg(mp);
	
    break;

    }

	return 1;
}


/******************************************************************/
static x25uwsrv(q)
/******************************************************************/

/*
    X.25 mux upper write service routine.  Since this interface is
    visible to user space, no trust is placed on the M_PROTO message format.
*/

queue_t *q;

{
    mblk_t  *mp, *mt, *glrec, *dat, *cudf;
    struct xgldbf *xug, *nxug;
    struct Q tempQ;
    struct i_xlistenf trec;
    int lenadd, lswitch, lindex, gentry, nref, lenOK;
    int result;
    unsigned char byte, cause, diag;
    struct xcallf FAR *call;
    struct xccnff FAR *caa;
    struct xdiscf FAR *dis;
    struct pvcattf FAR *pa;
    struct pvcdetf FAR *pd;
    mblk_t         *detbuf;

    extern int x25temp, x25ticks;

    if( (xug = (struct xgldbf *) q->q_ptr)==NULL )	/* Point at glue structure */
    {
	flushq(q,FLUSHALL);
	(void) putctl1(RD(q)->q_next,M_ERROR,ENXIO);/* No glue, M_ERROR */
	return 1;
    }

    if( q == x_ctlstrm )				/* Do clock ticks? */
    {
	if( x25ticks != x25temp )
	{
	    x25tick();				/* Fire timer code */
	}
    }

    while( mp = getq(q) )				/* Get message off queue */
    {
	if( ((struct xdataf FAR *)mp->b_rptr)->xl_type == XL_DAT )
	{
	    if( xug->gstate == G_ATTACHED )
	    {

		switch( ((struct xdataf FAR *)mp->b_rptr)->xl_command )
		{

		case N_Data:

		    if( pr_length(mp) == sizeof(struct xdataf) )
		    {
			result = N_putdata(xug->xnetref,mp);
			if( result != 0 )
			{
			    if( result > 0 )	/* Data pressure exerted */
			    {
				putbq(q,mp);	/* Put back & return */
				return 1;
			    }
			    else			/* Actually syserr */
			    {
				strlog(X25_STID,0,0,SL_ERROR,"X25: N_Data ref match");
				freemsg(mp);
			    }
			}
		    }
		    else				/* User length error */
		    {
			freemsg(mp);		/* Could be M_ERROR */
			abort_vc(xug);		/* Kill the connection */
		    }

		break;


		case N_DAck:

		    /* No length check - only carries function */
		    if( N_datack(xug->xnetref) != 0 )
		    {
			/* System error - reference */
			strlog(X25_STID,0,0,SL_ERROR,"X25: N_DAck ref match");
		    }
		    freemsg(mp);
		    
		break;

		case N_EData:

		    /* Peel off the data and send it */
		    if( (dat = mb_frag(mp)) != NULL )
		    {
			if( N_putexpdata(xug->xnetref,dat) != 0 )
			{			/* Interface Error */
			    strlog(X25_STID,0,0,SL_ERROR,"X25: N_EData ref match/IF error");
			    abort_vc(xug);
			    freemsg(dat);		/* or ref error */
			}
		    }
		    else
		    {
			abort_vc(xug);		/* Could be M_ERROR */
		    }
		    freemsg(mp);
		    
		break;

		case N_EAck:

		    /* No length check - only carries function */
		    if( N_expack(xug->xnetref) != 0 )
		    {
			/* System error - reference */
			strlog(X25_STID,0,0,SL_ERROR,"X25: N_EAck ref match");
		    }
		    freemsg(mp);

		break;

		default:

		    freemsg(mp);
		    abort_vc(xug);			/* Could be M_ERROR */

		break;

		}
	    }					/* End if G_ATTACHED */
	    else				/* else other states */
	    {
		if( xug->gstate == G_FREEZING )
		    go_frozen(q,mp);
		else if( xug->gstate == G_PVCWAITING )
		{
		    pvc_fn_err(xug);
		    freemsg(mp);
		}
		else
		    freemsg(mp);	        /* Discard XL_DAT - state */
	    }
						/* N.B. LISTEN trusted */
	}					/* End if XL_DAT */

	else if( ((struct xdataf FAR *)mp->b_rptr)->xl_type == XL_CTL )
	{
	    if( xug->gstate == G_FREEZING || xug->gstate == G_FROZEN )
		go_frozen(q,mp);
	    else
	        switch( (unchar)((struct xdataf FAR *)mp->b_rptr)->xl_command )
		{

		case N_CI:				/* Connect request */

		    if( xug->pvcflg )			/* Not a PVC function */
		    {
			freemsg(mp);
			pvc_fn_err(xug);		/* Abort or fail attach */
			continue;			/* With getq */
		    }

		    if (x_ctlstrm == NULL)		/* Clear the call */
		    {
		    	cause = LOCAL_PROCEDURE_ERROR;
			diag  = NS_NSAPPUNREACHABLE;    
			goto disreply;
		    }

		    if( xug->gstate == G_OPEN &&	/* State OK? and length */
		        pr_length(mp) == sizeof(struct xcallf) )
		    {
			xug->gstate = G_ATTACHED;	/* In case N_DI happens */
			call = (struct xcallf FAR *)mp->b_rptr;
			cudf = mb_frag(mp);

#ifdef DEBUG
	printf("CUDFF:: = %x",*cudf->b_rptr) ;
#endif
			if( (diag = (unsigned char) N_conreq( xug->gref,
			                     &xug->xnetref,
					     (unsigned char) call->CONS_call,
					     &call->calledaddr,
					     &call->callingaddr,
				         (call->negotiate_qos ? &call->qos : NULL),
					     cudf)) == 0 )
			{
#ifdef DEBUG
				printf("CUDF = %x",*cudf->b_rptr) ;
#endif
			    freemsg(mp);		/* Delete the header */
			}
			else				/* Refusal */
			{
			    xug->gstate = G_OPEN;	/* From ATTACHED */
			    cause = 0;			/* Diag already set */
			    m_freem(cudf);
			    goto disreply;
			}
		    }
		    else				/* State or msg wrong */
		    {
			if( xug->gstate == G_ATTACHED )	/* Wrong state for fn */
			{
			    freemsg(mp);
			    abort_vc(xug);
			}
			else if( xug->gstate == G_LISTENING )
			    go_frozen(q,mp);		/* Which frees mp buffer */
			else				/* Must be length error */
			{				/* Freeze states above */
			    cause = LOCAL_PROCEDURE_ERROR;
			    diag  = X25_LERR;
			    goto disreply;
			}
		    }
		    
		break;

		case N_CC:				/* Connect confirm */

		    if( xug->pvcflg )			/* Not a PVC function */
		    {
			freemsg(mp);
			pvc_fn_err(xug);		/* Abort or fail attach */
			continue;			/* With getq */
		    }

		    lenOK = (pr_length(mp)==sizeof(struct xccnff));

		    if( xug->gstate == G_ATTACHED )	/* Bad state / fn */
		    {
			freemsg(mp);
			abort_vc(xug);
		    }
		    else if( (xug->gstate == G_OPEN ||	/* Not listen channel */
		         xug->gstate == G_LISTENING) &&	/* or on that channel */
			                    lenOK != 0 )
		    {
			caa = (struct xccnff FAR *)mp->b_rptr;

			/*
			    Find connid on listener and unQ the entry for
			    connid. If found routine 'scan_ears' decrements
			    g_earuse
			*/
			glrec = find_connid(caa->conn_id,&gentry);

			if( glrec != NULL )		/* Found, glrec->msg */
			{
			    nxug = (&xgldb[gentry]);	/* Listener glue db */

			    if( nxug == xug )		/* Is this the original? */
			    {
				if( xug->g_earuse > 0 )	/* CI outstanding */
				{			/* FREEZE listener */
				    ON(&xug->gdbq,glrec);
				    xug->g_earuse++;
				    go_frozen(q,mp);	/* Clears all calls */
				    break;
				}
				else
				{
				    xug->gstate = G_OPEN;
				    purge_listens(xug->gref);
				}
			    }
			    else			/* Not on xug - error? */
			    {
				if( xug->gstate == G_LISTENING )
				{
				    /*
					Listener accepting another listen!
				    */
				    go_frozen(q,mp);	/* Calls rmCIDI */
				    freemsg(glrec);
				    break;
				}
			    }

			    if( not_abort(glrec) )	/* No abort queued */
			    {
				nref = ((struct i_xcallf FAR *)glrec->b_rptr)->holdns;
				freemsg(glrec);
				cudf= mb_frag(mp);
				freemsg(mp);		/* Delete old part */
				if( N_conrsp( xug->gref,
				              nref,
					          (unsigned char) caa->CONS_call,
					          &caa->responder,
					          (caa->negotiate_qos ? &caa->rqos : NULL),
					          cudf)==0 )
				{
				    xug->xnetref = nref;  /* Copy net reference */
				    xug->gstate = G_ATTACHED;
				}
				else
				{
				    /*
					This should be a panic - refs do not
					match
				    */
				    strlog(X25_STID,0,0,SL_ERROR,"X25: N_CC ref match");
				    m_freem(cudf);
				}
			    }
			    else			/* Abort queued */
			    {
				freemsg(mp);
#if ENDIAN_SWAP
				x25apiswap(glrec, UPWARD) ;
#endif
				qreply(xug->wq,glrec);	/* Fire up the abort */
			    }
			}
			else				/* Connid not found */
			{
			    if( xug->gstate == G_OPEN )
			    {
				cause = LOCAL_PROCEDURE_ERROR;
				diag  = X25_IDERR;
				goto disreply;
			    }
			    else			/* Is LISTENING */
			    {
				go_frozen(q,mp);	/* Calls rmCIDI */
			    }
			}
		    }
		    else
		    {				/* Discard - state or bad */
			if( lenOK == 0 )		/* IS LENGTH ERROR */
			    go_frozen(q,mp);
			else				/* CC OK but state wrong */
			    freemsg(mp);
		    }
		    
		break;

	/* ------ DISREPLY - Used by N_CI and N_CC ------ */

disreply:
		flushq(RD(q),FLUSHALL);		/* Clear out the read queue */
		m_freem( mb_frag(mp) );		/* Kill any data            */

		/*
		    Check whether we can reuse this message block for the
		    disconnect indication or we need to get a new one.
		*/
		lenOK = ((mp->b_datap->db_lim - mp->b_datap->db_base) >= sizeof( struct xdiscf) );

		if( !lenOK )
		{
		    if((mt = allocb(sizeof( struct xdiscf ),BPRI_HI)) != NULL)
		    {
		    	/*
			    If successful then set up the new message,
			    discard the old one and mark that we have the
			    correct length now.
			*/
		    	mt->b_datap->db_type = mp->b_datap->db_type;
			freemsg(mp);	
			mp = mt;	
			
			lenOK = 1;
		    }				
		}

		if ( lenOK )
		{
		    /*
			Message length long enough to code up disconnect
			indication
		    */
		    mp->b_rptr = mp->b_datap->db_base;
		    mp->b_wptr = mp->b_rptr + sizeof( struct xdiscf );
		    ((struct xdiscf FAR *)mp->b_rptr)->xl_type    = XL_CTL;
		    ((struct xdiscf FAR *)mp->b_rptr)->xl_command = N_DI;
		    ((struct xdiscf FAR *)mp->b_rptr)->cause      = cause;
		    ((struct xdiscf FAR *)mp->b_rptr)->diag       = diag;
		    ((struct xdiscf FAR *)mp->b_rptr)->originator = NS_PROVIDER;
		    ((struct xdiscf FAR *)mp->b_rptr)->reason     = NS_GENERIC;
		    ((struct xdiscf FAR *)mp->b_rptr)->responder.sn_id =
		    ((struct xdiscf FAR *)mp->b_rptr)->responder.aflags=
		    ((struct xdiscf FAR *)mp->b_rptr)->responder.DTE_MAC.lsap_len =
		    ((struct xdiscf FAR *)mp->b_rptr)->responder.nsap_len = '\0';
		}
		else
		{
		    /*
			Message not long enough to resuse as disconnect so
			code up abort indication instead. (Short).
		    */
		    ((struct xabortf FAR *)mp->b_rptr)->xl_type    = XL_CTL;
		    ((struct xabortf FAR *)mp->b_rptr)->xl_command = N_Abort;
		    mp->b_wptr     = mp->b_rptr + sizeof( struct xabortf );
		}

#if ENDIAN_SWAP
		x25apiswap(mp, UPWARD) ;
#endif
		qreply(q,mp);

		break;

		case N_RI:				/* Reset request */

		    if( xug->gstate == G_ATTACHED )	/* Make the request */
		    {
			if( pr_length(mp)==sizeof(struct xrstf) )
			{
			    cause = (unsigned char) ((struct xrstf FAR *)mp->b_rptr)->cause;
			    diag  = (unsigned char) ((struct xrstf FAR *)mp->b_rptr)->diag;
			    if( N_rstreq(  xug->xnetref,cause,diag) != 0 )
			    {
				/* Should be a system panic - no ref match */
				strlog(X25_STID,0,0,SL_ERROR,"X25: N_RI ref match");
			    }
			}
			else
			{
			    abort_vc(xug);
			}
		    }
		    else if( xug->gstate == G_PVCWAITING )
		    {
			pvc_fn_err(xug);
			/* Drop through to free message */
		    }

		    freemsg(mp);			/* Else/and free buffer */
		    
		break;


		case N_RC:				/* Reset confirm */

		    /* No length check - carries only function */
		    if( xug->gstate == G_ATTACHED )	/* Make the response */
		    {
			if( N_rstrsp(  xug->xnetref ) != 0 )
			{
			    /* Should be a system panic - no ref match */
			    strlog(X25_STID,0,0,SL_ERROR,"X25: N_RC ref match");
			}
		    }
		    else if( xug->gstate == G_PVCWAITING )
		    {
			pvc_fn_err(xug);
			/* Drop through to free message */
		    }

		    freemsg(mp);
		    
		break;


		case N_DI:

		    if( xug->pvcflg )			/* Not a PVC function */
		    {
			freemsg(mp);
			pvc_fn_err(xug);		/* Abort or fail attach */
			continue;			/* With getq */
		    }

		    dis = (struct xdiscf FAR *)mp->b_rptr;
		    lenOK = (pr_length(mp) == sizeof(struct xdiscf));

		    if( xug->gstate == G_ATTACHED )	/* Disconnect request */
		    {
			if( lenOK )
			{
			    cudf = mb_frag(mp);

			    if( N_disreq(xug->xnetref,
			        (unsigned char) dis->cause,
				    (unsigned char) dis->diag,
				    NULL,
				    (struct xaddrf FAR *)&dis->responder,
				    (struct xaddrf FAR *)NULL,
				    (mblk_t *)cudf) != 0 )	/* Sys error actually */
			    {
				strlog(X25_STID,0,0,SL_ERROR,"X25: N_DI request ref match");
				m_freem(cudf);
			    }
#ifdef INTERNAL_DISC
			    xug->gstate = G_OPEN;	/* Remain open */
#if ENDIAN_SWAP
			    x25apiswap(mp, UPWARD) ;
#endif
			    qreply(q,mp);		/* Respond */
#else
			    freemsg(mp);		/* Free buffer */
#endif
			}
			else
			{
			    freemsg(mp);		/* Ignore this disc and */
			    abort_vc(xug);		/* internally abort */
			}

			flushq(xug->rq,FLUSHALL);	/* Flush other queue */
		    }
		    else if( xug->gstate == G_LISTENING )	/* Rejection case */
		    {
			/*
			    Here checking the length is critical.  We need to
			    know the connid and the nature of the queued entry
			    for the connection.  In this case, the result of
			    an error is to go frozen i.e. never to return
			    unless the stream is closed.  I suspect that we
			    may remove this check if listening is to be
			    classed as privileged
			*/
			if( !lenOK )		/* Length fault */
			{			/* Disable listen & abort */
			    go_frozen(q,mp);	/* We cannot check id */
			    			/* Resource release in call */
			    break;		/* Process qd msgs */
			}

			/*
			    Otherwise, scan for the entry on this db
			*/
			glrec = scan_ears(dis->conn_id,xug);

			if( glrec != NULL )
			{
			    if( not_abort(glrec) )
			    {
				nref = ((struct i_xcallf FAR *)glrec->b_rptr)->holdns;

			    /*
				Should not check for subsequent listeners
				if the call is beoing deflected
			    */
			    if( dis->reason != NU_PERMANENT &&
			       ((nref>=FIRSTVC) && (nref<=isovcmax)) &&
			       (!(dis->indicated_qos && dis->qos.xtras.call_deflect)))
			    {
			        struct x25vcformat FAR *xvc;
			        mblk_t *hcopy;

			        /*
			            Index the vc record and check for subsequent
			            listeners on this address.
			        */
			        xvc=(struct x25vcformat FAR *) iso_vc[nref];
			        if( xvc->xtag==X25TAG && xvc->NSUattached!=0 )
			        {
			            if( user_on_NSAP(&xvc->called,
					             glrec->b_cont,
					             (xvc->xdomain != WAN80 ? 1 : 0),
					             &xvc->userref,
					             &xvc->qposition,
					             (ushort) (xvc->xbase->psdn_local.SNMODES&BAR_NONPRIV_LISTEN)) == 0 ) /* Yes */
			            {
			                /*
			                    Listener found so copy the saved
					    N_CI and send up on the apropriate 
					    stream.
			                */
			                if( (hcopy = copymsg(glrec)) != NULL )
	    			        {
			                    hcopy->b_wptr = hcopy->b_rptr + sizeof( struct xcallf );

			                    nxug = (&xgldb[xvc->userref]);
			                    ON(&nxug->gdbq,glrec);
							
			                    nxug->g_earuse++;
			                    putnext(nxug->rq,hcopy);
			                    freemsg(mp);
			                    break;
			                }
			            }
			        }
			    }

			    /*
			        Listener requests immediate clear or 
				call has been deflected or
				no further listener or alloc error or 
			        reference error.
			    */
			    cudf = mb_frag(mp);
			    if( N_disreq(nref,
			    		     (unsigned char) dis->cause,
			                 (unsigned char) dis->diag,
					         (struct qosformat *) (dis->indicated_qos ? &dis->qos : NULL),
			                 (struct xaddrf FAR *)&dis->responder,
			                 (struct xaddrf FAR *)&dis->deflected,
			                 (mblk_t *)cudf) != 0 )
			    {
			        m_freem(cudf);
			    }
#ifndef INTERNAL_DISC			  
			    else
			    {
				/*
				    Connection rejection so no user to
				    send N_DC to
				*/
				iso_vc[nref]->NSUattached=0;
			    }
#endif
			}
		        freemsg(mp);
		        freemsg(glrec);
			}
			else			/* Not found - delete */
			{			/* Disable listen & abort */
			    go_frozen(q,mp);	/* We cannot find id */
			    break;		/* Process qd msgs */
			}
		    }
		    else
			freemsg(mp);
		    
		break;


		case N_Xcanlis:

		    result = X25_LERR;	/* Till proven innocent */
		    
		    if (x_ctlstrm != NULL)
		    {
			/*
			    If we are a PVC then this is not a valid function.
			    So abort the attached user and skip to the next
			    message.
			*/
			if( xug->pvcflg )
			{
			    freemsg(mp);
			    pvc_fn_err(xug);
			    continue;		
			}

			/*
			    If the stream is in the correct state and no
			    connections are in progress on it, then purge
			    all its listen requests. 
			*/
			if( xug->gstate == G_LISTENING && xug->g_earuse == 0 )
			{
			    xug->gstate = G_OPEN;
			    xug->g_earmax = 0;
			    purge_listens(xug->gref);
			    result = 0;                  /* Success */
			}
		    }
		
		    ((struct xcanlisf FAR *)mp->b_rptr)->c_result = result;
#if ENDIAN_SWAP
		    x25apiswap(mp, UPWARD) ;
#endif
		    qreply(q,mp);

		break;


		case N_Xlisten:				/* Listen message */

		    tempQ.head = tempQ.tail = NULL;	/* Init Q structure */

		    if (x_ctlstrm == NULL)
			goto listerr;

		    if( xug->pvcflg )			/* Not a PVC function */
		    {
			freemsg(mp);
			pvc_fn_err(xug);		/* Abort or fail attach */
			continue;			/* With getq */
		    }

		    /*
			Only privileged users can issue priority listens.
			Barring of non privileged listens is on a subnetwork
			basis and is checked when the N_CI arrives.
		    */
		    if ( xug->xuid != 0 && mp->b_datap->db_type == M_PCPROTO )
		    {
			goto listerr;
		    }

		    if( xug->gstate == G_OPEN || xug->gstate == G_LISTENING )
		    {
		
			lswitch = X_cumode;		/* Obtain cudf mode */
			
			while( result = mb_nxtbyte(mp,(bufp_t)&byte))
			{
#ifdef DEBUG
printf("listen read byte x%x, mode x%x\n",byte,lswitch);
#endif
			    switch( lswitch )
			    {

			    case X_cumode:			/* Obtain l_cumode */

				trec.l_cumode = byte;
				
				if( byte == X25_DONTCARE   ||
				    byte == X25_STARTSWITH ||
				    byte == X25_IDENTITY )
				{
				    if( byte == X25_DONTCARE )
				    {
					lswitch = X_lmode;	/* Next l_mode */
					trec.l_total = 0;
				    }
				    else
					lswitch = X_culength;	/* Next l_culen */
				}
				else
				{
				    goto listerr;	/* Fail listen */
				}

			    break;

			    case X_culength:		/* Length to match */

				trec.l_culength = byte;
				trec.l_total    = byte;

				if( byte > 16 )		/* Too long */
				    goto listerr;
				else
				{
				    if( byte != 0 )
				    {
					lswitch = X_cubytes;
					lindex = 0;
				    }
				    else
					lswitch = X_lmode;
				}
			    break;

			    case X_cubytes:			/* Obtain cudf bytes */

				trec.l_cubytes[lindex++] = byte;
				if( lindex == trec.l_culength )
				    lswitch = X_lmode;
				    
			    break;

			    case X_lmode:			/* Obtain l_mode */

				trec.l_mode = byte;
				if( byte == X25_IDENTITY ||
				    byte == X25_DONTCARE ||
				    byte == X25_STARTSWITH )
				{
				    if( byte == X25_DONTCARE ) /* Back to start */
				    {
					lswitch = X_cumode;
					goto qlist;
				    }
				    else
					lswitch = X_ltype;	/* Next l_type */
				}
				else
				    goto listerr;	/* Fail listen */
				    
			    break;

			    case X_ltype:			/* Obtain l_type */

				trec.l_type = byte;

				if( byte == X25_DTE || byte == X25_NSAP )
				{
				    lswitch = X_llength;/* Next l_length */
				}
				else
				    goto listerr;	/* Fail listen */
				    
			    break;

			    case X_llength:			/* Obtain l_length */

				trec.l_length = byte;
				trec.l_total += byte;

				if( (trec.l_type == X25_DTE  && (int)byte <= DTEMAXSIZE) ||
				    (trec.l_type == X25_NSAP && byte <= 40) )
				{
				    if( byte != 0 )
				    {
					lswitch = X_ladd;	/* Next DTE/NSAP */
					lenadd  = (int)(byte+1)>>1;
					lindex  = 0;	/* For loading */
				    }
				    else
				    {
					lswitch = X_cumode;/* Start again */
					goto qlist;	/* Queue this entry */
				    }
				}
				else
				    goto listerr;	/* Fail listen */
				    
			    break;

			    case X_ladd:			/* Load address */

				trec.l_add[lindex++]=byte;

				if( lindex == lenadd )	/* DTE/NSAP fini */
				{
				    lswitch = X_cumode;	/* Next start again */
				    goto qlist;
				}
			    break;

qlist:			/*
			   Hold this entry, if resource available
			*/
			if( mt = allocb(sizeof(struct i_xlistenf),BPRI_MED) )
			{
			    /*
				Alter ordering key reflecting privileges.
				 -  PCPROTO > Root > User
			    */
			    if (mp->b_datap->db_type == M_PCPROTO)
				trec.l_total = PRIORITY_LISTEN;
	
			    else if ( xug->xuid == 0 )
				trec.l_total += ROOT_LISTEN;

			    mt->b_wptr = mt->b_rptr+sizeof(struct i_xlistenf);
			    bcopy((BUFP_T)&trec,
				  (BUFP_T)mt->b_rptr,sizeof(struct i_xlistenf));

				((struct i_xlistenf FAR *)mt->b_rptr)->l_snid =
				((struct xlistenf FAR *)mp->b_rptr)->l_snid ;
			    ON(&tempQ,mt);
			}
			else
			{
			    ((struct xlistenf FAR *)mp->b_rptr)->l_result = X25_LCANT;
			    goto qfree;		/* To free listen */
			}
			break;
			}			/* End lswitch */
		    }				/* End while getch */

		if( lswitch != X_cumode ||
		    tempQ.head == NULL )	/* Drop out of loop */
		    goto listerr;		/* Ended but not exhausted */
		else
		{				/* End of list */
		    while( mt = OFF(&tempQ) )   /* Requeue on database */
		        queue_listen(mt,xug->gref);

		    ((struct xlistenf FAR *)mp->b_rptr)->l_result = 0;
		    xug->gstate = G_LISTENING;	/* Change (remain) state */
		    xug->g_earmax = ((struct xlistenf FAR *)mp->b_rptr)->lmax;
		    goto lreply;
		}
	    }				/* End GOPEN/LISTEN */
	    else				/* Wrong state */
	    {
		goto listerr;
	    }


listerr:					/* Keep error etc nearby */
	    ((struct xlistenf FAR *)mp->b_rptr)->l_result = X25_LERR;
qfree:	    xpurgeQ((struct Q FAR *)&tempQ);			/* Free listen queue */
lreply:
	    mp->b_datap->db_type = M_PROTO;     /* May have been PCPROTO */
	    freemsg(mb_frag(mp));		/* Delete data part */
#if ENDIAN_SWAP
	    x25apiswap(mp, UPWARD) ;
#endif
	    qreply(q,mp);
	    break;


		case N_PVC_ATTACH:	/* PVC attach request */
					/* Keep ATTACH, warn in documents */

		    lenOK = ( pr_length(mp) == sizeof(struct pvcattf) );
		    pa = (struct pvcattf FAR *) mp->b_rptr;

		    if( lenOK != 0 && xug->gstate == G_OPEN )
		    {
			/* Prepare the DETACH for holding in the glue. */
			if( detbuf = allocb(sizeof(struct pvcdetf),BPRI_MED) )
			{
			    detbuf->b_datap->db_type = M_PROTO;
			    detbuf->b_wptr = detbuf->b_rptr + sizeof(struct pvcdetf);
			    pd=(struct pvcdetf FAR *) detbuf->b_rptr;
			    pd->xl_type    = XL_CTL;
			    pd->xl_command = N_PVC_DETACH;

			    switch( pa->result_code = att_PVC(xug,mp) )
			    {

			    case PVC_SUCCESS:	/* OK, send success message */

				xug->gstate = G_ATTACHED;
				xug->pvcflg = 1;
				ON(&xug->g_ear,detbuf);
				
			    break;

			    case PVC_WAIT:	/* OK, but wait till ready */

				xug->gstate = G_PVCWAITING;
				xug->pvcflg = 1;
				ON(&xug->g_ear,mp);
				mp=NULL;
				ON(&xug->g_ear,detbuf);

			    break;

			    default:		/* Refused for some reason */

				freemsg(detbuf);
				
			    break;
			    }
			}
			else
			    pa->result_code = PVC_CONGESTION;

			/* Return the original ATTACH if still extant */
			if( mp ) {
#if ENDIAN_SWAP
			    x25apiswap(mp, UPWARD) ;
#endif
			    qreply(q,mp);
			}
		    }
		    else		   /* State incorrect for PVC attach */
		    {
			goto duff_pvc_attempt;	/* Shared code for bad attach etc. */
		    }
		    
		break;				/* End of PVC attach case */

duff_pvc_attempt:			/* Attach etc. in error or state */
		    if( xug->gstate == G_ATTACHED )	/* May have PVC or connection */
		    {
			if( xug->pvcflg )
			    rmPVCusr(xug);		/* Detach the user */
			else				/* Disconnect the SVC */
			    (void) N_disreq(xug->xnetref,0,X25_ABORT,NULL,
				    (struct xaddrf FAR *)NULL,(struct xaddrf FAR *)NULL,NULL);
		    }
		    			/* ? resource in glue queues ? */
		    go_frozen(q,mp);	/* No more in any state allowed */
    				        /* Buffer is gone now */
		break;			/* End of duff pvc user shared code */

 
		case N_PVC_DETACH:			/* PVC detach request */

		    lenOK = ( pr_length(mp) == sizeof(struct pvcdetf) );
		    pd = (struct pvcdetf FAR *) mp->b_rptr;

		    if( xug->pvcflg != 0 && lenOK != 0 &&
		       (xug->gstate == G_ATTACHED || xug->gstate == G_PVCWAITING ) )
		    {
			xpurgeQ((struct Q FAR *)&xug->gdbq);		/* Free squirrel data */
			xpurgeQ((struct Q FAR *)&xug->g_ear);		/* Free detach etc. buffers */
			flushq(xug->rq,FLUSHALL);	/* Free waiting data */

			if( xug->gstate == G_ATTACHED )	/* Has PVC */
			{
			   rmPVCusr(xug);		/* Detach the pvc */
			}
			xug->pvcflg = 0;
			xug->gstate = G_OPEN;
			pd->reason_code = PVC_SUCCESS;	/* Respond to detach */
#if ENDIAN_SWAP
			x25apiswap(mp, UPWARD) ;
#endif
			qreply(q,mp);
		    }
		    else if(xug->gstate==G_OPEN && lenOK )
		    {				/* Respond with reason code */
			pd->reason_code=PVC_NOATTACH;
#if ENDIAN_SWAP
			x25apiswap(mp, UPWARD) ;
#endif
			qreply(q,mp);
		    }
		    else				/* Length or state incorrect */
		    {
			goto duff_pvc_attempt;		/* Shared code for bad attach etc. */
		    }				/* (buffer freed there) */
		    
		break;				/* End of PVC detach case */


		default:				/* Unknown! free */

		    freemsg(mp);
		    if( xug->gstate == G_ATTACHED )	/* Disconnect user */
			abort_vc(xug);
		    else if( xug->gstate == G_PVCWAITING )
			pvc_fn_err(xug);
		break;
		}					/* End switch on command */
	}					/* End if XL_CTL */
	else					/* Not CTL/DAT */
	    freemsg(mp);
    }   /* End while getq */

	return 1;

}


/******************************************************************/
static x25ursrv(q)
/******************************************************************/

/*
    X.25 mux upper read service routine.  Assumes only XL_DAT messages
    go onto the queue.
*/

queue_t *q;

{
    mblk_t  *mp;
    int datacount;
    struct xgldbf *xug;

    datacount = 0;
    while( (canput(q->q_next)) && (mp = getq(q)) )
    {
	if( ((struct xdataf FAR *)mp->b_rptr)->xl_command == N_Data )
	    datacount++;

#if ENDIAN_SWAP
	x25apiswap(mp, UPWARD) ;
#endif
	putnext(q,mp);
    }

    /* Flow considerations */

    if( datacount )
    {
	xug = (struct xgldbf *)q->q_ptr;		/* link to glue db */
	N_enableinput( xug->xnetref, datacount);
    }

	return 1;
}


/******************************************************************/
static x25lrput(q, mp)
/******************************************************************/

/*
    X.25 mux lower read put routine
*/

queue_t *q;
mblk_t  *mp;

{
unsigned int linkref;

#ifdef DEBUG
if( ((struct ll_msg FAR *)mp->b_rptr)->ll_type != LL_DAT ) /* Report only ctl */
{
printf("->lrput db_type %o type %d com %d stat %d : ",mp->b_datap->db_type,
	((struct ll_msg FAR *)mp->b_rptr)->ll_type,
	((struct ll_msg FAR *)mp->b_rptr)->ll_command,
	((struct ll_msg FAR *)mp->b_rptr)->ll_status);
    {
	unsigned char area[5];
	int l,kk;

	l=mb_bulen(mp);
	printf("len [%d]",l);
	if( (l=mb_bulen(mp))>5 )
	    l=5;
	getNbytes(mp,l,area);
	for(kk=0; kk<l; kk++)
	    printf(" %2x",area[kk]);
	printf("\n");
    }
}
#endif

    switch(mp->b_datap->db_type)
    {
    case M_PROTO:				/* The most common */

	if( ((struct ll_msg FAR *)mp->b_rptr)->ll_type != LL_REG )
	    putq(q,mp);			/* To service later */
	else
	{
	    /* Perform the LL_REG functions */
	    if( ((struct ll_reg FAR *)mp->b_rptr)->ll_regstatus == LS_SUCCESS )
	    {
		((struct xsndbf *)q->q_ptr)->regok = 1; /* Registered OK */
	    }
	    else
	    {
		strlog(X25_STID,0,0,SL_ERROR,"X.25: failed registration");
	    }
	    freemsg(mp);
	}
	
    break;

    case M_PCPROTO:				/* Do these now if recognised */

	if( ((struct ll_msg FAR *)mp->b_rptr)->ll_type == LL_CTL &&
	                                          q->q_ptr != NULL )
	{
	    linkref = ((struct xsndbf *)q->q_ptr)->linkref;
	    switch( ((struct ll_msg FAR *)mp->b_rptr)->ll_command )
	    {

	    case LC_CONOK:
	    case LC_CONNAK:
	    case LC_PRGBRA:
	    case LC_PRGKET:

		IND_toNS(linkref,mp);

	    break;

	    default:
	    
		freemsg(mp);
		
	    break;
	    }
	}
	else
	    freemsg(mp);			/* Ignore silently */

    break;

    default:				/* Ignore silently */

	freemsg(mp);
	
    break;

    }

	return 1;
}


/******************************************************************/
static x25lrsrv(q)
/******************************************************************/

/*
    X.25 mux lower read service routine
*/

queue_t *q;

{
    int linkref;
    mblk_t  *mp;

    if( q->q_ptr == NULL )
    {
	flushq(q,FLUSHALL);
	return 1;
    }

    linkref = ((struct xsndbf *)q->q_ptr)->linkref;

    while( mp = getq(q) )
    {
	IND_toNS(linkref,mp);
    }

	return 1;

}


/******************************************************************/
x25lwput(q, mp)
/******************************************************************/

/*
    X.25 mux lower write put routine (non priority)
*/

queue_t *q;
mblk_t  *mp;

{

#ifdef DEBUG
	if(mp->b_cont)
	{
		if(mp->b_cont->b_cont)
			printf("x25lwput = %x",*mp->b_cont->b_cont->b_rptr) ;
	}
#endif
#ifdef DEBUG
if( ((struct ll_msg FAR *)mp->b_rptr)->ll_type != LL_DAT ) /* Report only dat */
{
printf("<-lwput db_type %o type %d com %d stat %d : ",mp->b_datap->db_type,
	((struct ll_msg FAR *)mp->b_rptr)->ll_type,
	((struct ll_msg FAR *)mp->b_rptr)->ll_command,
	((struct ll_msg FAR *)mp->b_rptr)->ll_status);
    {
	unsigned char area[5];
	int l,kk;

	l=mb_bulen(mp);
	printf("len [%d]",l);
	if( (l=mb_bulen(mp))>5 )
	    l=5;
	getNbytes(mp,l,area);
	for(kk=0; kk<l; kk++)
	    printf(" %2x",area[kk]);
	printf("\n");
    }
}
#endif

    if( q->q_first == NULL && canput( q->q_next ) )
	putnext(q,mp);
   else
	putq(q,mp);

   	return 1;

}


/******************************************************************/
static x25lwsrv(q)
/******************************************************************/

/*
    X.25 mux lower write service routine
*/

queue_t *q;

{
    mblk_t  *mp;

    while( canput(q->q_next) && (mp=getq(q)) )
    {
	putnext(q,mp);
    }

	return 1;

}

#if ENDIAN_SWAP
/******************************************************************/
x25apiswap(mp,direction)
/******************************************************************/
mblk_t *mp ;
int direction ;
{
	S_X25_HDR *hdr = (S_X25_HDR *) mp->b_rptr ;

	if (direction == DOWNWARD) {
		SWAP32(hdr->xl_type) ;
		SWAP32(hdr->xl_command) ;
	}

	switch (hdr->xl_type) {
	case XL_DAT:
		if (hdr->xl_command == N_Data) {
			SWAP_RANGE(&((struct xdataf *)mp->b_rptr)->More,
				   &((struct xdataf *)mp->b_rptr)->setQbit) ;
		}
		else if (hdr->xl_command > N_EAck) {
			printf("Unusual data type %x\n", hdr->xl_command) ;
		}
		break ;
	case XL_CTL:
		switch (hdr->xl_command) {
		case N_RI:
			SWAP_RANGE(&((struct xrstf *)mp->b_rptr)->originator,
				   &((struct xrstf *)mp->b_rptr)->diag) ;
			break ;
		case N_DI: {
			struct xdiscf *dsc = (struct xdiscf *) mp->b_rptr ;

			SWAP_RANGE(&dsc->originator, &dsc->responder.DTE_MAC.lsap_len) ;
			SWAP_RANGE(&dsc->deflected, &dsc->deflected.DTE_MAC.lsap_len) ;
			SWAP_RANGE(&dsc->qos, &dsc->qos.lowprot_len) ;
			SWAP_RANGE(&dsc->qos.reqexpedited, &dsc->qos.xtras.nui_len) ;
			SWAP32(dsc->qos.xtras.rpoa_len) ;
			SWAP32(dsc->qos.xtras.cug_type) ;
			SWAP32(dsc->qos.xtras.reqcharging) ;
			SWAP32(dsc->qos.xtras.chg_cd_len) ;
			SWAP32(dsc->qos.xtras.chg_sc_len) ;
			SWAP32(dsc->qos.xtras.chg_mu_len) ;
			SWAP_RANGE(&dsc->qos.xtras.called_add_mod, &dsc->qos.xtras.called.lsap_len) ;
			SWAP_RANGE(&dsc->qos.xtras.call_deflect, &dsc->qos.xtras.cd_fac_len) ;
		  }
			break ;
		case N_DC: {
			struct xdcnff *dsc = (struct xdcnff *) mp->b_rptr ;

			SWAP_RANGE(&dsc->indicated_qos, &dsc->rqos.lowprot_len) ;
			SWAP_RANGE(&dsc->rqos.reqexpedited, &dsc->rqos.xtras.nui_len) ;
			SWAP32(dsc->rqos.xtras.rpoa_len) ;
			SWAP32(dsc->rqos.xtras.cug_type) ;
			SWAP32(dsc->rqos.xtras.reqcharging) ;
			SWAP32(dsc->rqos.xtras.chg_cd_len) ;
			SWAP32(dsc->rqos.xtras.chg_sc_len) ;
			SWAP32(dsc->rqos.xtras.chg_mu_len) ;
			SWAP_RANGE(&dsc->rqos.xtras.called_add_mod, &dsc->rqos.xtras.called.lsap_len) ;
			SWAP_RANGE(&dsc->rqos.xtras.call_deflect, &dsc->rqos.xtras.cd_fac_len) ;
		  }
			break ;
		case N_CI: {
			struct xcallf *call = (struct xcallf *) mp->b_rptr ;

			SWAP_RANGE(&call->conn_id, &call->calledaddr.DTE_MAC.lsap_len) ;
			SWAP_RANGE(&call->callingaddr, &call->callingaddr.DTE_MAC.lsap_len) ;
			SWAP_RANGE(&call->qos, &call->qos.lowprot_len) ;
			SWAP_RANGE(&call->qos.reqexpedited, &call->qos.xtras.nui_len) ;
			SWAP32(call->qos.xtras.rpoa_len) ;
			SWAP32(call->qos.xtras.cug_type) ;
			SWAP32(call->qos.xtras.reqcharging) ;
			SWAP32(call->qos.xtras.chg_cd_len) ;
			SWAP32(call->qos.xtras.chg_sc_len) ;
			SWAP32(call->qos.xtras.chg_mu_len) ;
			SWAP_RANGE(&call->qos.xtras.called_add_mod, &call->qos.xtras.called.lsap_len) ;
			SWAP_RANGE(&call->qos.xtras.call_deflect, &call->qos.xtras.cd_fac_len) ;
		  }
			break ;
		case N_CC: {
			struct xccnff *ccf = (struct xccnff *) mp->b_rptr ;

			SWAP_RANGE(&ccf->conn_id, &ccf->responder.DTE_MAC.lsap_len) ;
			SWAP_RANGE(&ccf->rqos, &ccf->rqos.lowprot_len) ;
			SWAP_RANGE(&ccf->rqos.reqexpedited, &ccf->rqos.xtras.nui_len) ;
			SWAP32(ccf->rqos.xtras.rpoa_len) ;
			SWAP32(ccf->rqos.xtras.cug_type) ;
			SWAP32(ccf->rqos.xtras.reqcharging) ;
			SWAP32(ccf->rqos.xtras.chg_cd_len) ;
			SWAP32(ccf->rqos.xtras.chg_sc_len) ;
			SWAP32(ccf->rqos.xtras.chg_mu_len) ;
			SWAP_RANGE(&ccf->rqos.xtras.called_add_mod, &ccf->rqos.xtras.called.lsap_len) ;
			SWAP_RANGE(&ccf->rqos.xtras.call_deflect, &ccf->rqos.xtras.cd_fac_len) ;
		  }
			break ;
		case N_PVC_ATTACH: {
			struct pvcattf *pvc = (struct pvcattf *) mp->b_rptr ;

			SWAP_RANGE(&pvc->lci, &pvc->result_code) ;
		  }
			break ;
		case N_PVC_DETACH:
			SWAP32(((struct pvcdetf *)mp->b_rptr)->reason_code) ;
			break ;
		case N_Xlisten:
			SWAP32(((struct xlistenf *)mp->b_rptr)->lmax) ;
			SWAP32(((struct xlistenf *)mp->b_rptr)->l_result) ;
			SWAP32(((struct xlistenf *)mp->b_rptr)->l_snid) ;
			break ;
		case N_Xcanlis:
			SWAP32(((struct xcanlisf *)mp->b_rptr)->c_result) ;
			break ;
		default:
			if (hdr->xl_command > N_PVC_DETACH)
				printf("unknown ctrl type %x\n", hdr->xl_command) ;
		}
		break ;
	default:
		/* Do nothing */
		printf("Unknown SWAP type (%x,%x)\n", hdr->xl_type, hdr->xl_command) ;
		break ;
	}

	if (direction == UPWARD) {
		SWAP32(hdr->xl_type) ;
		SWAP32(hdr->xl_command) ;
	}
}
#endif

