#ident "$Id: x25mbu.c,v 1.2 1995/04/06 16:41:50 garyp Exp $"
/*****************************************************************************
 * Copyright (c) 1988-95 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

/*******************************************************************
 *
 *  SpiderX25 X25 PLP Driver
 *
 *  Copyright 1991 Spider Systems Limited
 *
 *  X25MBU.C
 *
 *
 *******************************************************************/

/*
 *	x25mbu.c
 *	 /projects/common/PBRAIN/SCCS/pbrainF/dev/sys/x25/0/s.x25mbu.c
 *	@(#)x25mbu.c	1.9
 *
 *	Last delta created	11:28:51 10/30/91
 *	This file extracted	18:33:00 11/19/92
 *
 */

#include "x25_inc.h"

#ifndef NULL
#define NULL 0
#endif

/* ---------------------------------------------------------------------------
   Routines employed by the X25 software to perform single character
   read and write on a stream chain.  Depends on the user initialising the
   procedures using the setup procedures mb_gset and mb_pset (g=get and
   p=put).  Get and Put routines return 0 on failure, 1 otherwise.  In the
   put set of routines, it is assumed that the chain is already constructed
   and no attempt is made to obtain more mblocks for subsequent puts.
   ------------------------------------------------------------------------ */

/************************************************************************/
int mb_bulen(m)
/************************************************************************/

/*
    Returns length of data in an mbuf chain. Uses streams utiity
    msgdsize (could be macro)
*/

register mblk_t *m;

{
    register int len;

    if( m != NULL )
    {
	len = msgdsize(m);
    }
    else
	len=0;
	
    return(len);
}


/************************************************************************/
void mb_gset(xvc,m)
/************************************************************************/

/*
    Initialises the internal vars for read.
*/

register struct x25vcformat FAR *xvc;
mblk_t *m;

{
    register mblk_t *n;


    n=m;

    /*
	Skip through to a data byte - does not remove empty blocks
    */
    while( n!=NULL &&
          (n->b_datap->db_type != M_DATA || n->b_wptr == n->b_rptr))
    {
	n=n->b_cont;
    }
	
    if( n )
    {
	xvc->mb_gbuf=n;                   /* Retain pointer to this block */
	xvc->mb_gl=(int)(n->b_wptr - n->b_rptr); /* Retain block data length     */
	xvc->mb_gind=m;                   /* Retain pointer to message    */
    }
    else
    {
	xvc->mb_gbuf=(mblk_t *)0;
    }
}


/************************************************************************/
int FAR * mtod(m)
/************************************************************************/

/*
    Returns a pointer to the first data byte or NULL
*/

mblk_t *m;

{
    register mblk_t *n;

    n=m;

    /*
	Skip through to a data byte
    */
    while( n!=NULL && 
           (n->b_datap->db_type != M_DATA || n->b_wptr == n->b_rptr))
    {
	n=n->b_cont;
    }
    
    if( n )
    {
	return((int FAR *)n->b_rptr);	/* First M_DATA byte here */
    }
    else
	return( (int FAR *) 0 );
}


/************************************************************************/
int mb_1stbyte(m)
/************************************************************************/

/*
    Returns (int) 1st byte in mbuf chain or -1. Is non-destructive.
*/

register mblk_t *m;

{
    register int FAR * n;

    if( n=mtod(m) )
    {
	return( (int) (*n) );
    }
    else
    {
	return(-1);
    }
}


/************************************************************************/
int mb_getch(xvc,ch)
/************************************************************************/

/*
    Reads character to ch, 0 fails, 1 succeeds
    DESTRUCTIVE READ, but no freeb calls!
*/

register struct x25vcformat FAR *xvc;
bufp_t	ch;

{
    register mblk_t *mb_gbuf;

    mb_gbuf = xvc->mb_gbuf;

    if(mb_gbuf)
    {
	*ch = ( *mb_gbuf->b_rptr );          /* Get byte and          */
	mb_gbuf->b_rptr++;                   /* Increment data offset */
	if( --xvc->mb_gl == 0 )              /* If no more, chain     */
	{
	                                     /* Empty - reset ptrs    */
	    mb_gbuf->b_rptr=mb_gbuf->b_wptr=mb_gbuf->b_datap->db_base;
            mb_gset(xvc,xvc->mb_gind);
	}
        return(1);
    }
    else
    {
	*ch='\0';
	return(0);
    }
}


/************************************************************************/
void mb_pset(xvc,m)
/************************************************************************/

/*
    Initialise internal vars for PUT.
*/

struct x25vcformat FAR *xvc;
mblk_t *m;

{
    register mblk_t *n;
    register mblk_t *m_candidate;

    n=m;
    m_candidate=NULL;

    while( n!=NULL )
    {
	if( n->b_datap->db_type != M_DATA ||
	    n->b_wptr == n->b_datap->db_lim )
	{
	    m_candidate=NULL;
	    n=n->b_cont;                /* Fragment not data or full - next */
	}
	else
	{
	    m_candidate=n;              /* Might have candidate    */
	    n=n->b_cont;                /* Any data after this?    */
	    
	    while( n!=NULL )
	    {
		if( n->b_wptr != n->b_rptr || n->b_wptr == n->b_datap->db_lim )
		{
		    m_candidate=NULL;   /* Data follows this one   */
		    break;
		}
		else
		{
		    /* Do not touch following data's pointers? */
		    n=n->b_cont;        /* Empty, test next        */
		}
	    }
	}
    }
    /*
	If not NULL, calculate the PUT position
    */
    xvc->mb_pbuf=m_candidate;
}


/************************************************************************/
int mb_putch(xvc,ch)
/************************************************************************/

/*
    Put character into chain, 1 success, 0 failure
*/

struct x25vcformat FAR *xvc;
unsigned char ch;

{
    register mblk_t *mb_pbuf;

    mb_pbuf = xvc->mb_pbuf;
    
    if( mb_pbuf )
    {
	/*
	    Check if PUT possible
	*/	
	if( mb_pbuf->b_wptr != mb_pbuf->b_datap->db_lim )
	{
	    *mb_pbuf->b_wptr = ch;	/* PUT byte     */
	    mb_pbuf->b_wptr++;
	    return(1);
	}
	else
	{
	    mb_pbuf=mb_pbuf->b_cont;	/* Chain along  */
	    if(mb_pbuf)			/* Recall check in pset! */
	    {
		*mb_pbuf->b_wptr = ch;
		mb_pbuf->b_wptr++;
		return(1);
	    }
	    else
	    {
		mb_pbuf = NULL;		/* Stop put here */
		return(0);
	    }
	}
    }
    return(0);
}


/************************************************************************/
bufp_t mb_byteaddr(xvc)
/************************************************************************/

/*
    Return address of where next byte is put
*/

struct x25vcformat FAR *xvc;

{
    register mblk_t *temp;
    register mblk_t *mb_pbuf;

    mb_pbuf = xvc->mb_pbuf;

    if( mb_pbuf )
    {
	/*
	    Check if PUT possible
	*/
	if( mb_pbuf->b_wptr != mb_pbuf->b_datap->db_lim )
	{
	    return(mb_pbuf->b_wptr);
	}
	else
	{
	    if( (temp=mb_pbuf->b_cont) )   /* Chain along  */
	    {
		/*
		    pset check should be good enough to have confidence
		*/
		return(temp->b_wptr);
	    }
	    else
		return( (bufp_t) 0 );
	}
    }
    return( (bufp_t) 0 );
}


/************************************************************************/
int mb_nxtbyte(m,ch)
/************************************************************************/

/*
    Reads next character to ch, 0 fails, 1 succeeds
    DESTRUCTIVE READ.
*/

mblk_t *m;
unsigned char FAR *ch;

{
    register mblk_t *n;

    n=m;				/* Skip through to a data byte */

    while( n != NULL &&
          (n->b_datap->db_type != M_DATA || n->b_wptr == n->b_rptr))
    {
	n=n->b_cont;
    }
		
    if( n )
    {
	*ch = *n->b_rptr++;
	return(1);
    }
    else
	return(0);
}


/************************************************************************/
mblk_t *mb_zapdata(n)
/************************************************************************/

/*
    Equates all M_DATA block rptr and wptr to base.
    Return the pointer to the message.
*/

mblk_t *n;

{
    register mblk_t *m;

    m=n;
    while( m!=NULL )
    {
	if (m->b_datap->db_type == M_DATA )
	{
	    m->b_rptr = m->b_wptr = m->b_datap->db_base;
	}
	m=m->b_cont;
    }
    return(n);
}


/************************************************************************/
mblk_t *mb_frag(m)
/************************************************************************/

/*
    Returns a pointer to the data part and breaks the message buffer
    at that point - none -> NULL
    WARNING - does not work on pure M_DATA message.
*/

mblk_t *m;

{
    register mblk_t *n, *t;

    n=m;

    while( n!=NULL )
    {
	if( n->b_cont != NULL )
	{
	    if( n->b_cont->b_datap->db_type == M_DATA )
	    {
		t = n->b_cont;
		n->b_cont = NULL;
		return(t);
	    }
	}
	else
	    break;
	    
	n=n->b_cont;
    }
    return(NULL);
}


/************************************************************************/
void mb_join(m1,m2)
/************************************************************************/

/*
    Joins two messages (second parameter to the first)
    WARNING - does not check recipient not NULL
*/

register mblk_t *m1, *m2;

{
    while( m1->b_cont != NULL )        /* Find end    */
	m1 = m1->b_cont;

    m1->b_cont = m2;                   /* Concatenate */
}


/************************************************************************/
void m_freem(xx)
/************************************************************************/

/*
    If pointer not NULL, free the message
*/

mblk_t *xx;

{
    if( xx != NULL )
	freemsg(xx);
}


/************************************************************************/
void getNbytes(mp,NUM,area)
/************************************************************************/

/*
    Retrieves NUM bytes from a buffer chain which has already been
    checked for length and places into the area indicated.
*/

mblk_t        *mp;
int           NUM;
unsigned char *area;

{
    mblk_t *n;
    int i, dl;

    if( NUM==0 )
	return;

    /*
	Skip through to a data byte
    */
    n=mp;		
    while( n!=NULL && 
          (n->b_datap->db_type != M_DATA || n->b_wptr == n->b_rptr))
    {
	n=n->b_cont;
    }

    if( n )
    {
	i = 0;
	while( i < NUM )
	{
	    dl = (int)(n->b_wptr - n->b_rptr);	/* Obtain this message length */
	    if( dl > (NUM - i) )	/* Mod the amount to take     */
		dl = ( NUM - i );

	    /*
		Copy over the data
	    */
	    bcopy( (bufp_t)n->b_rptr, (bufp_t)area, dl );

	    n = n->b_cont;		/* Chain to next buffer  */
	    area += dl;			/* Bump the area pointer */
	    i    += dl;			/* and length copied out */
	}
    }
}


/************************************************************************/
void putNbytes(mp,NUM,area)
/************************************************************************/

/*
    Inserts NUM bytes into a buffer chain which has already been
    checked for length.
*/

mblk_t        *mp;
int           NUM;
unsigned char *area;

{

mblk_t *n;
int i, dl;

    if( NUM==0 )
	return;

    /*
	Skip through to a data byte
    */
    n=mp;
    while( n!=NULL && 
          (n->b_datap->db_type != M_DATA || n->b_wptr == n->b_rptr))
    {
	n=n->b_cont;
    }

    if( n )
    {
	i = 0;
	while( i < NUM )
	{
	    dl = (int)(n->b_wptr - n->b_rptr);	/* Obtain this message length */
	    if( dl > (NUM - i) )	/* Mod the amount to take     */
		dl = ( NUM - i );

	    /*
		Copy over the data
	    */
	    bcopy( (bufp_t)area, (bufp_t)n->b_rptr, dl );   

	    n = n->b_cont;		/* Chain to next buffer  */
	    area += dl;			/* Bump the area pointer */
	    i    += dl;			/* and length copied out */
	}
    }
}
