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

/*
 *	x25txout.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25txout.c
 *	@(#)x25txout.c	1.25
 *
 *	Last delta created	16:10:20 4/20/92
 *	This file extracted	18:33:06 11/19/92
 *
 */
#include "x25_inc.h"

#include "x25pti.h"
#include "x25clock.h"
#include "x25form.mac"
#include "x25mbu.h"

#ifndef NULL
#define NULL 0
#endif

extern mblk_t *allocb(), *dupb();

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

            Routines to send packets out to the link level.

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


/************************************************************************/
void to_link_level(xvc,x)
/************************************************************************/

/*
    Pass frame to level2, noting the state of the PLP, and 'limbo' status.
*/

struct x25vcformat FAR *xvc;
mblk_t *x;

{
    register struct lsformat FAR *lsp;

    if( (lsp=xvc->lsaplink) != NULL &&          /* I.e. not in 'limbo'   */
         lsp->lstate == L_connected )           /* and state OK          */
    {
	/*
	    LL_datareq does the following 

	    x->b_datap->db_type = M_PROTO;
	    x->b_wptr = x->b_rptr + sizeof(struct ll_msg);
	    msg = x->b_rptr;
	    msg->ll_type       = LL_DAT;
	    msg->ll_command    = LC_DATA;
	    msg->ll_connID     = lsp->lconnid;
	    msg->ll_yourhandle = lsp->lowhandle;
	*/
	if( LL_datareq(lsp,x) != 0)
	    return;                            /* Quick exit - OK       */
    }

   L3monitor(L3outbad, xvc->xu_ident);
   m_freem(x);
}


/************************************************************************/
int short_out(xvc,pt)
/************************************************************************/

/*
    Send short frame. If buffer granted, return 1 otherwise return 0.
*/

register struct x25vcformat FAR *xvc;
unsigned char pt;

{
    register mblk_t      *hdr, *dat;

    if( (hdr=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL)
    {
	if( (dat=allocb(3,BPRI_MED))!=NULL)
	{
	    /*
		Load GFI, LCI and PTI to a buffer.
		For CLC set A-bit in GFI if required.
	    */
	    *dat->b_wptr = xvc->lgngfi;
	    if (pt == CLC_PTI && (xvc->xbase->SUB_MODES&SUB_TOA_NPI_FMT))
		*dat->b_wptr |= 0x80;
	    dat->b_wptr++;
	    (*dat->b_wptr++) = xvc->lcn;
	    (*dat->b_wptr++) = pt;
	    hdr->b_cont = dat;             /* Chained */

	    to_link_level(xvc,hdr);

#ifdef DEBUG
	    if (pt == CLC_PTI)	
		strlog(X25_STID,xvc->xu_ident,2,SL_TRACE,
		       "CLC   Out : '%x' lci %03x",xvc->xu_ident,xvc->lci);
	    else if (pt == RSC_PTI)
		strlog(X25_STID,xvc->xu_ident,3,SL_TRACE,
		       "RSC   Out : '%x' lci %03x",xvc->xu_ident,xvc->lci);
	    else if (pt == INC_PTI)
		strlog(X25_STID,xvc->xu_ident,5,SL_TRACE,
		       "INTC  Out : '%x' lci %03x",xvc->xu_ident,xvc->lci);
#endif
	    return(1);
	}
	else
	    m_freem(hdr);
    }
    return(0);
}


/************************************************************************/
int fctl_out(xvc,pt,nrout)
/************************************************************************/

/*
    Send extended RR/RNR frame. If buffer granted, return 1 otherwise 0.
*/

register struct x25vcformat FAR *xvc;
unsigned char pt, nrout;

{
    register mblk_t      *hdr, *dat;

    /*
	Load LCI, GFI and PTI and the 'nrout' (NR)to a buffer.  return 1
	if buffer granted otherwise 0.
    */
    if( (hdr=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL)
    {
	if( (dat=allocb(4,BPRI_MED))!=NULL)
	{
	    (*dat->b_wptr++)=xvc->lgngfi;
	    (*dat->b_wptr++)=xvc->lcn;
#ifdef MOD8ONLY
	    (*dat->b_wptr++)=(pt|(nrout<<5));
#else
	    if( xvc->xmodulo == 7 )
	    {
		(*dat->b_wptr++)=(pt|(nrout<<5));
	    }
	    else
	    {
		(*dat->b_wptr++)=pt;
		(*dat->b_wptr++)=(nrout<<1);
	    }
#endif
	    hdr->b_cont=dat;            /* Chain */
	    to_link_level(xvc,hdr);
	    return(1);
	}
	else
	    m_freem(hdr);
    }
    return(0);
}


/************************************************************************/
void Tx_Qdata(xvc)
/************************************************************************/

/*
   Transmits, perhaps fragmented, user data.
*/

register struct x25vcformat FAR *xvc;

{
    register mblk_t *hdr, *temp, *tempA, *tempB, *dathdr;
    int buflen, len, surlen, first, datalen;

    xvc->xusrevent&= ~xDTout;                      /* Cancel data event */

    first=0;                                       /* Set when frame sent */
    while( xvc->acksoutstanding < (int)xvc->lwsize )    /* Window is open    */
    {
	/*
	    Data may be queued both as 'datatogo' and/or the transmit
	    queue.  Also M-bit conditions may prevent transmission.
	    It will be assumed and set up that the 'datatogo' message will
	    always be preceded by a message block to contain the link level
	    message parameters.
	    The following conditional MAY permit 0 length data packets.

	*/
	while( xvc->datatogo==NULL ||
              (((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit == 0 &&
	       ((struct xdataf FAR *)xvc->datatogo->b_rptr)->More    != 0 &&
	        (buflen=mb_bulen(xvc->datatogo))<(int)(unsigned int)xvc->lpkt) )
	{
	    /*
		Before sending, need to extract at least one buffer from the
		transmit queue
	    */
	    tempA=xvc->x25txqhead;
	    if( tempA!=NULL )
	    {
		/*
		    Something on the queue, what happens depends on whether
		    the datatogo pointer was already set for a fragment
		*/
		if( xvc->datatogo == NULL )
		{
		    /*
			Nothing there already so acquire a header
			for datatogo
		    */
		    if( (tempB=allocb(sizeof(struct ll_msg),BPRI_MED)) != NULL )
		    {
			/*
			    Chain the data part and adjust the queue head
			*/
			tempB->b_cont = (mblk_t *) mb_frag(tempA);
			xvc->x25txqhead=tempA->b_next;
			tempA->b_next = (mblk_t *) 0 ;

			/*
			    Decrement queue size. If empty then
			    tidy up. Note flow released when data goes
			*/
			if( xvc->x25txqhead==NULL )
			{
			    xvc->x25txqtail=NULL;
			    xvc->x25txqsize=0;
			}
			else
			    xvc->x25txqsize--;

			/*
			    Copy across the M,D,and Q bit settings. Free
			    the old header and set net datatogo
			*/
			((struct xdataf FAR *)tempB->b_rptr)->More =
			((struct xdataf FAR *)tempA->b_rptr)->More;

			((struct xdataf FAR *)tempB->b_rptr)->setQbit =
			((struct xdataf FAR *)tempA->b_rptr)->setQbit;

			((struct xdataf FAR *)tempB->b_rptr)->setDbit =
			((struct xdataf FAR *)tempA->b_rptr)->setDbit;

			m_freem(tempA);
			xvc->datatogo = tempB;
		    }
		    else
			goto dt_crisis;   /* Low buffers reset event */
		}
		else
		{
		    /*
			Data was waiting to go so concatenation required
		    */
		    mb_join(xvc->datatogo,mb_frag(tempA));

		    /*
			Copy across the M,D,and Q bit settings and
			adjust queue head.
		    */
		    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->More =
		    ((struct xdataf FAR *)        tempA->b_rptr)->More;

		    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->setQbit =
		    ((struct xdataf FAR *)        tempA->b_rptr)->setQbit;

		    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit =
		    ((struct xdataf FAR *)        tempA->b_rptr)->setDbit;

		    xvc->x25txqhead=tempA->b_next;  
		    tempA->b_next = (mblk_t *) 0 ;

		    /*
			Decrement queue size. If empty then
			tidy up. Note flow released when data goes
		    */
		    if( xvc->x25txqhead==NULL )
		    {
			xvc->x25txqtail=NULL;
			xvc->x25txqsize=0;
		    }
		    else
			xvc->x25txqsize--;
			
		    m_freem(tempA);      /* Free old header    */
		}
	    }
	    else
	    {
		/*
		    Q head is NULL, nothing can go yet
		*/
		goto flcheck;
	    }
	}                          /* End while loop setting up datatogo */

	/*
	    At this point, we have a buffer to send which is set up to
	    have an ll_msg header.  It may require fragmentation.
	    Before fragmenting or sending the data, ensure that we can get
	    the buffer(s) necessary to  hold the X25 level 3/2 headers which
	    is concatenated before sending to the link level.
	*/
	if( (dathdr=allocb(4,BPRI_MED))!=NULL )
	{
	    buflen=mb_bulen(xvc->datatogo);

	    if( (unsigned int)buflen>(unsigned int)xvc->lpkt )
	    {
		/*
		    Fragmentation required so acquire new header
		*/
		if( (tempA=allocb(sizeof(struct ll_msg),BPRI_MED)) == NULL )
		{
		    /*
			Free of pti fragment and reset event,
		    */
		    m_freem(dathdr);
		    goto dt_crisis;
		}

		/*
		    Resources allocated, so continue to fragment
		*/
		len=0;
		temp=xvc->datatogo->b_cont;

		while(1)
	        {
		    len += (int)(temp->b_wptr-temp->b_rptr);
		    
		    if( len==xvc->lpkt )
		    {
			/*
			   Break the chain. Point at second fragment and
			   terminate first fragment
			*/
			tempA->b_cont=temp->b_cont; 
			temp->b_cont=NULL;             

			/*
			    Copy across the M,D,and Q bit settings.
			    Point to datatogo being sent and save second
			    part.
			*/
			((struct xdataf FAR *)        tempA->b_rptr)->More =
			((struct xdataf FAR *)xvc->datatogo->b_rptr)->More;
			((struct xdataf FAR *)xvc->datatogo->b_rptr)->More = 1;
			
			((struct xdataf FAR *)        tempA->b_rptr)->setQbit =
			((struct xdataf FAR *)xvc->datatogo->b_rptr)->setQbit;
			
			((struct xdataf FAR *)        tempA->b_rptr)->setDbit =
			((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit;
			((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit = 0;
			
			hdr=xvc->datatogo;
			xvc->datatogo=tempA;
			break;
		    }
		    else if( (unsigned int)len>(unsigned int)xvc->lpkt )
		    {
			/*
			    A buffer is required for fragmentation. In
			    STREAMS we dup the block requiring to be fragmented
			*/
			if( (tempB=dupb(temp)) == NULL )
			{
			    /*
				Free of alloced buffers and reset event,
			    */
			    m_freem(dathdr);
			    m_freem(tempA);
			    goto dt_crisis;
			}
			else
			{
			    /*
				Adjust the data pointers
			    */
			    surlen=(len-xvc->lpkt);
			    temp->b_wptr=tempB->b_rptr = temp->b_wptr - surlen;

			    /*
				Terminate the message and affix to new
				header
			    */
			    tempB->b_cont = temp->b_cont;
			    temp->b_cont = NULL;
			    tempA->b_cont= tempB;
			    
			    /*
				Copy across the M,D,and Q bit settings.
				Point to datatogo being sent and save second
				part.
			    */
			    ((struct xdataf FAR *)        tempA->b_rptr)->More =
			    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->More;
			    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->More = 1;
			    
			    ((struct xdataf FAR *)        tempA->b_rptr)->setQbit =
			    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->setQbit;
			    
			    ((struct xdataf FAR *)        tempA->b_rptr)->setDbit =
			    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit;
			    ((struct xdataf FAR *)xvc->datatogo->b_rptr)->setDbit = 0;
			    
			    hdr = xvc->datatogo;
			    xvc->datatogo = tempA;
			    break;
			}
		    }
		    else
		    {
			/*
			    More data to scan
			*/		    
			temp=temp->b_cont;
		    }
		}                       /* End 'while'          */
	    }
	    else                                    /* <=lpkt - can send    */
	    {
		/*
		    Len <= lpkt hence can send. Point to data going
		    and reset datatogo
		*/
		hdr=xvc->datatogo;
		xvc->datatogo=NULL;
	    }

	    /*
		Fragmentation, if necessary, now complete, prepare and
		send the frame. If Dbit set and application conf required
		then update count and mark sequence number so we can tell
		when its been acked.
	    */
	    if ( ((struct xdataf FAR *)hdr->b_rptr)->setDbit &&
	                           xvc->loc_Dsupport == RC_CONF_APP)
	    {
		xvc->Dreq_sent_cnt++;
		xvc->Dreq_sent_seq[xvc->nstogo] = 1;
	    }
	
	    /*
		Fragmentation, if necessary, now complete, prepare and
		send the frame. First look at the T24 and T25 clock
	    */
	    if( first==0 )                     /* Do once only     */
	    {
		first=1;

		/* Start T25 if acksoutstanding */
		if( xvc->acksoutstanding==0 )
		    startT25(xvc);

		/*
		    Check whether any acks are available to be piggy
		    backed on this data packet
		*/
		if( xvc->nrsent!=xvc->nrtogo )
	        {
#ifdef MOD8ONLY
		    xvc->ftoack -= ((xvc->nrtogo-xvc->nrsent)&7);
#else
		    xvc->ftoack -= ((xvc->nrtogo-xvc->nrsent)&xvc->xmodulo);
#endif
		    xvc->nrsent=xvc->nrtogo;

		    /*
			If there are still frames we need to ack then
			restart T24 else stop it.
		    */
		    if( xvc->ftoack != 0 )
			startT24(xvc);
		    else
			stopT24(xvc); 
		}
		/* (Otherwise, the T24 clock continues) */
	    }

	    /*
		Log event for anyone who is interested.
	    */
/* CDK
	    strlog(X25_STID,xvc->xu_ident,6,SL_TRACE,"DATA  Out : '%x' lci %03x ns/nr %04x",
                            xvc->xu_ident,xvc->lci,xvc->nstogo<<8|xvc->nrtogo);

*/
	    /*
		Insert the message for the X.25 header etc.
	    */
	    dathdr->b_cont = hdr->b_cont;
	    hdr->b_cont    = dathdr;

	    /*
	        This data is going, no matter what.  Increment
	        the data octet statistics.
            */
	    xvc->out_octets += mb_bulen(dathdr) ;

	    /*
		Set up the gfi acording to D and Q bit requests
	    */
	    (*dathdr->b_wptr) = xvc->lgngfi;
	    if( ((struct xdataf FAR *)hdr->b_rptr)->setQbit )
		(*dathdr->b_wptr) |= 0x80;

	    if ( ((struct xdataf FAR *)hdr->b_rptr)->setDbit )
		(*dathdr->b_wptr) |=  0x40;

	    dathdr->b_wptr++;
	    
	    (*dathdr->b_wptr++)=xvc->lcn;

#ifdef MOD8ONLY
	    (*dathdr->b_wptr++)=(xvc->nstogo<<1)+(xvc->nrtogo<<5) |
	                 (((struct xdataf *)hdr->b_rptr)->More ? 0x10 : 0X00);
	    xvc->nstogo=(xvc->nstogo + 1)&7;
#else
	    if( xvc->xmodulo == 7 )
	    {
		(*dathdr->b_wptr)=(xvc->nstogo<<1)+(xvc->nrtogo<<5);

		if( ((struct xdataf FAR *)hdr->b_rptr)->More )
		    (*dathdr->b_wptr) |= 0x10;
		    
		dathdr->b_wptr++;

		xvc->nstogo=(xvc->nstogo + 1)&7;
	    }
	    else
	    {
		(*dathdr->b_wptr++)=(xvc->nstogo<<1);

		(*dathdr->b_wptr)=(xvc->nrtogo<<1);
		
		if( ((struct xdataf FAR *)hdr->b_rptr)->More )
		    (*dathdr->b_wptr) |= 1;

		dathdr->b_wptr++;
		    
		    
		xvc->nstogo=(xvc->nstogo + 1)&127;
	    }
#endif
	    xvc->acksoutstanding++;

	    to_link_level(xvc,hdr);
       	    L3monitor(dt_out, xvc->xu_ident);
	    xvc->out_pdu++ ;
	}
	else                                 /* Buffer crisis try again */
	{
	    goto dt_crisis;
	}
    }

flcheck:                                     /* Check user flow state  */

    if( xvc->Udatawaiting &&
        ((unsigned int)(xvc->acksoutstanding + xvc->x25txqsize) < (unsigned int)xvc->lwsize) )
    {
	xvc->Udatawaiting = 0;
	FL_touser(xvc);
    }
    return;

dt_crisis:
    /*
	Needed a buffer, try again Flow enabled when data goes
    */
    xvc->xusrevent|=xDTout;
}


/************************************************************************/
int Tx_EDdata(xvc)
/************************************************************************/

/*
    Transmit expedited data.
*/

register struct x25vcformat FAR *xvc;

{
    register mblk_t        *hdr, *dat;
    int l;

    xvc->xusrevent&= ~xINTout;
 
    if( xvc->xEDbuff != NULL )
    {
	if( (xvc->xflag&INTOUTSTANDING)==0 )
	{
	    if( (l = 32 - mb_bulen(xvc->xEDbuff)) < 0 )
		(void) adjmsg(xvc->xEDbuff,l);

	    if( (hdr=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL)
	    {
		if( (dat=allocb(3,BPRI_MED))!=NULL)
		{
		    (*dat->b_wptr++)=xvc->lgngfi;
		    (*dat->b_wptr++)=xvc->lcn;
		    (*dat->b_wptr++)=INT_PTI;
		    dat->b_cont=xvc->xEDbuff;         /* Link the chains */
		    xvc->xEDbuff=NULL;                /* Null pointer    */
		    xvc->xflag|=INTOUTSTANDING;       /* Mark substate i2 */
		    xvc->tx_int_pkts++;
		    startT26(xvc);
		    /* AckE sent to upper when confirmed */
		    hdr->b_cont=dat;                  /* Chain            */

		    to_link_level(xvc,hdr);
#ifdef DEBUG
		    strlog(X25_STID,xvc->xu_ident,5,SL_TRACE,"INT Out : '%x' lci %03x",
		                                       xvc->xu_ident,xvc->lci);
#endif
                    L3monitor(ed_out, xvc->xu_ident);
		    return(1);
		}
		else
		{
		    m_freem(hdr);
		    xvc->xusrevent|=xINTout;          /* Crisis try again */
		}
	    }
	    else
	    {
		xvc->xusrevent|=xINTout;          /* Crisis try again */
	    }
	}
	else
	{
	    system_error(Ubadint);
	}
    }
    else
	system_error(Unoint);
	
    return(0);
}


/************************************************************************/
void Tx_INC(xvc)
/************************************************************************/

/*
    Respond to interrupt indication
*/

register struct x25vcformat FAR *xvc;

{

    xvc->xusrevent&= ~xINCout;

    if( short_out(xvc,INC_PTI)==0 )
	xvc->xusrevent|=xINCout;               /* No buffer, try again */

}


/************************************************************************/
void maybe_ack(xvc)
/************************************************************************/

/*
    Cause an acknowledgment frame to be sent if the window status changes.
*/

register struct x25vcformat FAR *xvc;

{
    register int    pool_stressed, primed;
    register unsigned char nextstatus, rrnr;

    pool_stressed=buffs_status();           /* Check buffer pool state       */
    nextstatus = xvc->DTEbusy;              /* No change unless changed      */
    primed = 0;                             /* No ack unless primed non zero */

    if( nextstatus == 0 &&( pool_stressed !=0 || xvc->remcredit<0 ) )
    {
	nextstatus = 1;                     /* Going busy                    */
	primed = 1;                         /* Tell remote end               */
    }
    else if( nextstatus!=0 && ( pool_stressed==0 && xvc->remcredit>=0 ) )
    {
	nextstatus = 0;                     /* Going receive ready           */
	primed = 1;                         /* Tell remote end               */
    }
    else if( xvc->nrtogo != xvc->nrsent )
    {
	primed = 1;                         /* Tell remote end of window     */
    }

    if( primed )                            /* Ack going?                    */
    {
	if( nextstatus == 0 )
	    rrnr = RR_PTI;
	else
	    rrnr = RNR_PTI;
	    
	if( fctl_out( xvc, rrnr , xvc->nrtogo ) == 1 )
	{
	    if( nextstatus==0 )
	    {
		L3monitor(rr_out, xvc->xu_ident);
	    }
	    else
	    {
		L3monitor(rnr_out, xvc->xu_ident);
	    }
	    stop_ackdelay(xvc);        /* Ack gone, but may restart below */
#ifdef MOD8ONLY
	    xvc->ftoack -= ((xvc->nrtogo - xvc->nrsent)&7);
#else
	    xvc->ftoack -= ((xvc->nrtogo - xvc->nrsent)&xvc->xmodulo);
#endif
	    /*
		Examine T24 in the light of this ack. If no more frames to
		ack then stop the timer. Otherwise if we have acknowledged
		some frames then restart the timer else leave it running.
	    */
	    if( xvc->ftoack )
	    {
		if( xvc->nrtogo != xvc->nrsent )
		    startT24(xvc);
	    }
	    else
		stopT24(xvc);
	    
	    xvc->nrsent        = xvc->nrtogo;
	    xvc->DTEbusy       = nextstatus;
	    xvc->ack_immediate = 0;
	}
	else
	{
	    /*
		Could not get a buffer, start the ack timer.
	    */
	    start_ackdelay(xvc);
	    return;
	}
    }

    /*
	Now examine the pool status and start the delay timer if
	buffers are getting low
    */
    if( pool_stressed )
	start_ackdelay(xvc);
}


/************************************************************************/
int clear_request(xvc)
/************************************************************************/

/*
    Send clear request. Return 1 if successful, 0 otherwise
*/

register struct x25vcformat FAR *xvc;

{
    register mblk_t        *hdr;
#ifndef SOH
    extern mblk_t *make_c_packet(struct x25vcformat FAR *,unsigned char);
#endif

    /*
	NB diag/reason stored before call for transmission after buff crises.
	First check whether NS user data is allowed on the clear
    */
    if( xvc->Udiscdata!=NULL )
    {
	/*
	    Check clear user data allowed re 1984 and 1980
	*/
	if( (xvc->ISOflag&(FSELECT | FSRRESP))==0 ||
	     (xvc->xdomain==WAN80 && (xvc->xflag&caareq)!=0) )
	{
	    m_freem(xvc->Udiscdata);   /* No - free the held buffer */
	    xvc->Udiscdata=NULL;
	}
    }

    if( (hdr=make_c_packet(xvc,(unsigned char) CLR_PTI)) != NULL )
    {
#ifdef DEBUG
	strlog(X25_STID,xvc->xu_ident,2,SL_TRACE,"CLR   Out : '%x' lci %03x C/D %04x",
                         xvc->xu_ident,xvc->lci,xvc->discause<<8|xvc->disdiag);
#endif
	   
	to_link_level(xvc,hdr);
	return(1);
    }
    return(0);  /* Failure */
}


/************************************************************************/
int Tx_RSC(xvc)
/************************************************************************/

/*
   Send reset confirmation (1-success, zero otherwise)
*/

register struct x25vcformat FAR *xvc;

{
    if( short_out(xvc,RSC_PTI) )
    {
	return(1);
    }
    else
	return(0);
}


/************************************************************************/
int Tx_RST(xvc,c,d)
/************************************************************************/

/*
    Send reset request. return 1 if successful, 0 otherwise
*/

register struct x25vcformat FAR *xvc;
unsigned char c, d;

{
    register mblk_t  *hdr, *dat;

    xvc->rstcause=c;
    xvc->rstdiag =d;          /* For transmission after buffer crisis */

    if( (hdr=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL)
    {
	if( (dat=allocb(5,BPRI_MED))!=NULL)
	{
	    (*dat->b_wptr++)=xvc->lgngfi;
	    (*dat->b_wptr++)=xvc->lcn;
	    (*dat->b_wptr++)=RST_PTI;
	    (*dat->b_wptr++)=c;
	    (*dat->b_wptr++)=d;
	    hdr->b_cont=dat;              /* Chain */

#ifdef DEBUG
	    strlog(X25_STID,xvc->xu_ident,3,SL_TRACE,"RST   Out : '%x' lci %03x C/D %04x",
                                               xvc->xu_ident,xvc->lci,c<<8|d);
#endif
				       
	    to_link_level(xvc,hdr);
	    L3monitor(rst_out, xvc->xu_ident);
	    return(1);
        }
	else
	    m_freem(hdr);
    }

    return(0);  /* Failure */
}


/************************************************************************/
int restart_request(lsp,c,d,h)
/************************************************************************/

/*
    Send restart, storing diags.  Return 0 for resource (or mb_putch) failure.
*/

register struct lsformat FAR *lsp;
unsigned char c, d;
mblk_t *h;

{
    register mblk_t     *dat;


    lsp->rescause=c;
    lsp->resdiag =d;


    /*
	Check if supplied buffer is large enough
    */
    if ( h != NULL )
    {
	dat = h->b_cont;

	if( dat==NULL || (dat->b_datap->db_lim - dat->b_datap->db_base < 5))
	{
	    m_freem(h);
	    h = NULL;
	}
    }

    if ( h == NULL )
    {
	if ((h=allocb(sizeof(struct ll_msg),BPRI_HI))==NULL )
	{
	    return(0);  /* failed */
	}
    	else
	{
	    h->b_datap->db_type = M_PROTO;
			
	    if ( (dat=allocb(5,BPRI_HI))==NULL )
	    {
		m_freem(h);
		return(0);
	    }
	    else
	    {
		h->b_cont = dat;       /* Chain */
	    }
	}
    }

    /* We have a buffer */

#ifdef MOD8ONLY
    *dat->b_wptr++ = G_8;
#else
    *dat->b_wptr++ = (unsigned char) lsp->lbase->THISGFI;
#endif

    *dat->b_wptr++ = '\0';
    *dat->b_wptr++ = RES_PTI;

    /* *dat->b_wptr = lsp->rescause; */

    *dat->b_wptr++ = '\0';
    *dat->b_wptr++ = lsp->resdiag;

    /*
	LL_datareq does the following ...
	h->b_wptr = h->b_rptr + sizeof(struct ll_msg);
	msg = h->b_rptr;
	msg->ll_type       = LL_DAT;
	msg->ll_command    = LC_DATA;
	msg->ll_connID     = lsp->lconnid;
	msg->ll_yourhandle = lsp->lowhandle;
    */	

#ifdef DEBUG
    strlog(X25_STID,lsp->lu_ident,4,SL_TRACE,"REST  Out : '%x' C/D %04x",
	                                               lsp->lu_ident,c<<8|d);
#endif

    if( LL_datareq(lsp,h) == 0 )
	m_freem(h);
	
    L3monitor(res_out, lsp->lu_ident);

    return(1);
}


/************************************************************************/
void confirm_restart(lsp,h)
/************************************************************************/

/*
    Send restart confirmation, a buffer must  be passed in the call.
*/

register struct lsformat FAR *lsp;
mblk_t *h;

{
    register mblk_t *dat;


    dat = h->b_cont;

#ifdef MOD8ONLY
    *dat->b_wptr++ = G_8;
#else
    *dat->b_wptr++ = (unsigned char) lsp->lbase->THISGFI;
#endif

    *dat->b_wptr++ = 0;
    *dat->b_wptr++ = REC_PTI;

    /*
	LL_datareq does the following ...
	h->b_wptr = h->b_rptr + sizeof(struct ll_msg);
	msg = h->b_rptr;
	msg->ll_type       = LL_DAT;
	msg->ll_command    = LC_DATA;
	msg->ll_connID     = lsp->lconnid;
	msg->ll_yourhandle = lsp->lowhandle;
    */

#ifdef DEBUG
    strlog(X25_STID,lsp->lu_ident,4,SL_TRACE,"RESTC Out : '%x'",
	                                              lsp->lu_ident);
#endif
	
    if( LL_datareq(lsp,h) == 0 )
	m_freem(h);
}


/************************************************************************/
void Tx_DIAG(lsp,err,npars,b1,b2,b3)
/************************************************************************/

/*
    Transmit a DIAGNOSTIC packet - if preprocessor option permits.
*/

register struct lsformat FAR *lsp;
unsigned char err, npars, b1, b2, b3;

{
    register mblk_t     *hdr, *dat;

    /* Tuning mode selects option */

    L3monitor(dg_out, lsp->lu_ident);

    if( (lsp->lbase->psdn_local.SNMODES&USE_DIAG) == 0 ||
       ((lsp->ldomain != LAN84 && lsp->ldomain != LAN88) && lsp->DTEmode == 1 ) )
    {    
	/* Not to DCE */
	return;
    }

    if( (hdr=allocb(sizeof(struct ll_msg),BPRI_MED))!=NULL)
    {
	if( (dat=allocb(7,BPRI_MED))!=NULL)
	{
#ifdef MOD8ONLY
	    (*dat->b_wptr++)=G_8;
#else
	    (*dat->b_wptr++)=(unsigned char) lsp->lbase->THISGFI;
#endif
	    (*dat->b_wptr++)=0;
	    (*dat->b_wptr++)=DIAG_PTI;
	    (*dat->b_wptr++)=err;
	    (*dat->b_wptr++)=b1;
	    (*dat->b_wptr++)=b2;
	    (*dat->b_wptr++)=b3;
	    dat->b_wptr = dat->b_rptr + (4 + npars);
	    hdr->b_cont = dat;

	if( LL_datareq(lsp,hdr) == 0 )
	    m_freem(hdr);

	}
	else
	    m_freem(hdr);
    }
}
