/*********************************************************************** 
* NOTICE
* All files contained on this disk are subject to the licensing conditions
* issued by MOTOROLA Inc.
*
* All files are copyright 1993 by MOTOROLA Inc. 
************************************************************************/

/********************************************************
 * File:	smc_r.c
 *
 * Description:
 *	This file contains routines used for handling
 *	data reception form all modes of SMCs
 *	(UART, transparent and GCI).
 *
 * Routines:
 *	smc_fill_rtab
 *	smc_replenish_rxbd
 *	smc_return_to_pool
 *
 * Author:
 *	Ofrit Lesser
 ********************************************************/


#include "gct.h"
#include "types.h"
#include "mtypes.h"
#include "msg.h"
#include "bss.h"
#include "quicc.h"
#include "status.h"



/********************************************************
 * routine:	smc_fill_rtab
 *
 * description:
 *	Fill the receive buffer table with new buffers as possible.
 *	Called after collecting received frames (XXX_rxfr),
 *	when returning buffers to the receive pool
 *	(smc_return_to_pool) and when creating a pool (smc_pool).
 *
 * arguments:
 *	wa		points to the channels work area
 *
 * return code:
 *	The number of buffers added to the rx table.
 *
 * side effects:
 *	Increment (modulo the receive table size) the quicc_pbd
 *	index each time a new buffer is linked to the table.
 *
 ********************************************************/
smc_fill_rtab(wa)
register WORK_AREA *wa;
{
	register int count;
	register R_FRAME *r;
	register QUICC_BD *rbd;		/* BD in table */

	r = wa->pool_q;
	count = 0;
	while( wa->rfree ){
		/*
		 * never use the R_FRAME that is currently
		 * at the end of the pool
		 */
		if( !r->nextb )
			break;

		rbd = wa->quicc_pbd;

		/*
		 * prepare status bits, leaving the
		 * wrap bit unaltered
		 */
		rbd->status &= R_W;
		rbd->status |= wa->BDrx;
		rbd->length = 0;
		rbd->buf = r->buf;


		/*
		 * prepare r_frame for a future data indication
		 * set type field and data ptr (may have been
		 * advanced when removing headers)
		 */
		r->hdr.type = RX_IND;
		r->boff = 0;
		r->flags |= LINKED_TO_TABLE;

		/*
		 * advance to next frame, BD
		 */
		wa->rfree--;
		if( rbd->status & R_W )
			wa->quicc_pbd = SMC_RBD_ADDR(wa);
		else
			wa->quicc_pbd++;
		r = r->nextb;
		count++;

		rbd->status |= R_E;	/* ready for reception */
	}
	wa->pool_q = r;
	wa->pool_cnt -= count;
	return(count);
}



/********************************************************
 * routine:	smc_replenish_rxbd
 *
 * description:
 *	Replenish the Rx BD table.
 *
 * arguments:
 *	wa		points to the work area
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
smc_replenish_rxbd(child)
register CHILD *child;
{
	short flgs;
	int i;
	register WORK_AREA *wa;

	wa = (WORK_AREA *)child->work_area;
	/*
	 * link frames to BD table
	 */
	i = smc_fill_rtab(wa);
	flgs = child->flags & (POOL_EMPTY | LOCAL_BUSY);

	/*
	 * if there are less than minpool buffers
	 * in the rx pool, issue a busy interrupt
	 * and reduce the maximum frame length.
	 */
	if( wa->pool_cnt < wa->minpool ){
		if( !(flgs & POOL_EMPTY) ){
			wa->pram->pothers.idma_smc.psmc.u.mrblr = wa->rlen;
			child->flags |= POOL_EMPTY;
		}
	} else if( flgs & POOL_EMPTY ){
		child->flags &= ~POOL_EMPTY;
		wa->pram->pothers.idma_smc.psmc.u.mrblr = wa->fr_lng;
	}

	/*
	 * clear local busy flag
	 * if any buffers were added to the rx BD table
	 */
	if( i )
		child->flags &= ~LOCAL_BUSY;

	/*
	 * local busy situation may have either been
	 * cleared or detected
	 */
	if( flgs ){
		if( !(child->flags & (POOL_EMPTY | LOCAL_BUSY)) )
			return(LOCAL_BUSY_CLEARED);
		else
			return(0);
	} else if( child->flags & (POOL_EMPTY | LOCAL_BUSY) )
			return(LOCAL_BUSY_DETECTED);
		else
			return(0);
}


/********************************************************
 * routine:	smc_return_to_pool
 *
 * description:
 *	Return the given R_FRAMES to the receive pool.
 *	All frames are automatically assumed to be from the
 *	same SMC port. Buffers of a frame are linked via nextb.
 *
 * arguments:
 *	r		points to the first r_frame in the chain
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
smc_return_to_pool(r, child)
register R_FRAME *r;
CHILD *child;
{
	register WORK_AREA *wa;
	register R_FRAME *r1, *r2;
	int i, status;
	MSG *m;
	GCT *gct;
	extern MSG *getm();

	GETGCT(gct);
	wa = (WORK_AREA *)child->work_area;
	r1 = r;			/* save r in temp */
	r2 = wa->rtp_q;		/* save rtp in temp */

	/*
	 * find the end of the frame
	 * NOTE: be careful not to zero the RX_POOL
	 * bit which should already be set in flags.
	 */
	r->flags &= ~LINKED_TO_TABLE;
	r->hdr.status = 0;
	i = r->hdr.id;
	wa->pool_cnt++;
	driver_trace(RTP, r->hdr.id, r );
	while( r->nextb ){
		wa->pool_cnt++;
		r = r->nextb;
		r->hdr.status = 0;
		r->hdr.id = i;
		r->flags &= ~LINKED_TO_TABLE;
		driver_trace(RTP, r->hdr.id, r );
	}

	/*
	 * to be correct, the nextb of the old rtp
	 * should only be written after the rtp has
	 * been written with its new value
	 */
	wa->rtp_q = r;		/* last buffer */
	r2->nextb = r1;

	/*
	 * keep receive BD table stoked up
	 */
	status = smc_replenish_rxbd(child);
	if( status ){
		driver_trace(BUSY_IND, r->hdr.id, status);
		m = getm(BUSY_IND, r->hdr.id, 0, 0);
		if( m ){
			m->hdr.status = status;
			if( (*gct->send)(child->upper, m) )
				relm((HDR *)m);
		}
	}
}
