/*********************************************************************** 
* 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:	bisync_r.c
 *
 * Description:
 *	The driver routines for handling receive
 *	on an BISYNC port.
 *
 * Routines:
 *	bisync_rxfr
 *	bisync_handle
 *	bisync_busy
 *	bisync_rxch
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/


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


extern driver_trace(int type, int id, volatile int par);

/********************************************************
 * routine:	bisync_rxfr
 *
 * description:
 *	This is reached after being sent a RX_IND message
 *	by the interrupt routine (interrupt on QUICC
 *	receiving a complete BISYNC frame).
 *	The receive buffer table is flushed of all BISYNC
 *	frames received: these frames are sent to the
 *	port's upper layer.
 *	The RX_IND message sent is only a 32-bit code (not an
 *	allocated MSG structure) and therefore must
 *	not be relm-ed. Its structure corresponds to
 *	the first 32-bits of a normal HDR structure.
 *
 * arguments:
 *	h		the event message received
 *	child		points to the child structure
 *
 * return code:
 *
 * side effects:
 *	Advance quicc_rbd each time a
 *	received buffer is collected.
 *	The ccr interrupt must be re-enabled after
 *	all received messages have been collected.
 *
 ********************************************************/
bisync_rxfr(h, child)
HDR *h;
CHILD *child;
{
	GCT *gct;
	register WORK_AREA *wa;
	register unsigned short status;		/* status of BD */
	register R_FRAME *r1, *r2, *r3;
			/* r1 = first r_frame in frame */
			/* r2 = current r_frame */
			/* r3 = previous r_frame */
	struct quicc_bd *nextBD;	/* next BD in frame */
	int flen;		/* accumulative frame length */
	MSG *m;
	int eofr;	/* boolean: end of frame recognized */
	int bcount;	/* number of BDs in this frame */
	int crc;
	register int i;
	register unsigned char *c;
	unsigned long mode;
	extern MSG *getm();

	GETGCT(gct);
	wa = (WORK_AREA *)child->work_area;
	mode = wa->regs->scc_gsmra;

	/*
	 * clear event bit
	 * this clears indications of old (masked) events
	 */
	wa->regs->scc_scce = BISYNC_RX;

	/*
	 * if receive is disabled,
	 * return immediately
	 */
	if( child->flags & RX_DISABLE )
		if( child->flags & RX_ACTIVATE )
			child->flags &= ~RX_ACTIVATE;
		else
			return;

	if( !wa->rx_q->nextb ){
		wa->regs->scc_sccm |= BISYNC_RX;
		return;
	}

	/*
	 * rbd address next BD for receive
	 */
	nextBD = wa->quicc_rbd;
	status = nextBD->status;
	r1 = r2 = r3 = wa->rx_q;
	flen = 0;
	bcount = 0;
	eofr = 0;
	while( !(status & R_E) ){
		/*
		 * first check that r2 has been
		 * linked to the rx BD table
		 */
		if( !(r2->flags & LINKED_TO_TABLE) )
			break;

		if( !(status & R_B) )
			status &= ~R_BCS;

		bcount++;

		/*
		 * see if the buffer includes the BCS;
		 * if so: adjust the buffer length
		 */
		r2->blen = nextBD->length;
		/*
		 * update accumulative frame length
		 * on the last BD in a frame, the length
		 * is the length of the entire BISYNC frame.
		 */
		flen += r2->blen;

		if( status & R_B ){
			/*
			 * CRC may be split between last two buffers
			 */
			crc = ((mode&BI_CRC)? 1: 2);
			flen -= crc;
			if( r2->blen >= crc )
				r2->blen -= crc;
			else {
				r3->blen -= (crc - r2->blen);
				r2->blen = 0;
				/* release emptied buffer */
				r3->nextb = r2->nextb;
				r2->nextb = 0;
				return_to_pool(r2, child);
				r2 = r3;
			}
		}

		/*
		 * clear bit #7 if in 7 bit character mode
		 * (if LRC is used)
		 */
		if( mode&BI_CRC ){
			c = r2->buf + r2->boff;
			for(i = 0; i < r2->blen; i++)
				(*c++) &= 0x7f;
		}

		/*
		 * if end of frame ...
		 * This means that a BCS has been received.
		 */
		if( status & (R_B | R_C) ){
			/*
			 * update status and length
			 * in new received frame,
			 * zero accumulative length
			 * for next frame.
			 */
			r1->flen = flen;	/* frame total length */
			r3 = r2;
			r2 = r2->nextb;
			r3->nextb = 0;	/* no more buffs in this frame */

			/*
			 * prepare for next R_FRAME
			 */
			flen = 0;

			driver_trace(RX_IND, r1->hdr.id, (int)r1);
			/*
			 * pass r_frame to upper layer
			 * (subject to error mask).
			 * buffers are linked via nextb
			 */
			if( status & R_ERROR ){
				driver_trace(RX_ERROR, h->id, (int)nextBD);
				r1->hdr.status = RECEIVE_ERROR;
				r1->hdr.err_info = status & R_ERROR;
				wa->rerr_cnt++;
				if( (status&R_ERROR) & ~wa->rxerr_mask )
					return_to_pool(r1, child);
				else
					if( (*gct->send)(child->upper, r1) )
						return_to_pool(r1, child);
			} else
				if( (*gct->send)(child->upper, r1) )
					return_to_pool(r1, child);
			r1 = r3 = r2;
			wa->rcount++;
			eofr = 1;
		} else {
			r3 = r2;
			r2 = r2->nextb;	/* next R_FRAME for rx */
		}

		/*
		 * advance rbd (to next Rx BD in table)
		 */
		if( status & R_W )
			nextBD = TBD_ADDR(wa);
		else
			nextBD++;

		if( eofr ){
			wa->rfree += bcount;
			bcount = eofr = 0;
			wa->quicc_rbd = nextBD;
		}

		status = nextBD->status;	/* update status */
	}

	wa->rx_q = r1;

	/*
	 * replenish the rx BD table
	 */
	status = replenish_rxbd(child);
	if( status ){
		driver_trace(BUSY_IND, h->id, status);
		m = getm(BUSY_IND, h->id, 0, 0);
		if( m ){
			m->hdr.status = status;
			if( (*gct->send)( child->upper, m) )
				relm((HDR *)m);
		}
	}

	/*
	 * re-enable interrupts
	 * setting the mask will result in an interrupt
	 * if any frames were received during the time
	 * the mask was zero. therefore, frames will not
	 * go unnoticed if received after exiting the while
	 * loop above and before the mask bit is set.
	 */
	wa->regs->scc_sccm |= BISYNC_RX;
	if( !(child->flags & LOCAL_BUSY) )
		wa->regs->scc_sccm |= BISYNC_BUSY;
}



/********************************************************
 * routine:	bisync_rxch
 *
 * description:
 *	This routine is called from the interrupt routine
 *	for the bisync handler. It's duty is to handle
 *	the first bytes of an incoming block (enter transparent
 *	mode, reset BCS calculation, etc.).
 *
 * arguments:
 *	bd		the last BD received by the QUICC
 *	wa		points to the child's work area
 *	channel		the SCC channel number
 *
 * return code:
 *	non-zero	force end of message
 *
 * side effects:
 *
 ********************************************************/
bisync_rxch(bd, wa, channel)
register QUICC_BD *bd;
register WORK_AREA *wa;
unsigned short channel;
{
	register int j;
	register char bchar;
	char dle, soh, stx;
	char tr;
	unsigned char mask;
	GCT *gct;
	unsigned short mode;

	GETGCT(gct);
	soh = ((GWA *)gct->driver->work_area)->soh;
	stx = ((GWA *)gct->driver->work_area)->stx;
	dle = wa->pram->pscc.b.bdle;

	/* "can't" happen */
	if( bd->length == 0 )
		return;

	mode = wa->regs->scc_gsmra;
	/* prepare mask for 7 bit characters */
	mask = (mode&BI_CRC)? 0x7f: 0xff;

	/* first character may be DLE */
	if( (bd->buf[0] & mask) == dle )
		tr = 1;
	else
		tr = 0;

	/* wait for next character to be received */
	j = 0;
	while( bd->length < (tr+1) ) j++;
	if( j > 1000 )
		return;

	bchar = bd->buf[tr] & mask;
	if( (bchar == soh) || (bchar == stx) ){
		if( tr )
			wa->regs->scc_gsmra |= BI_TRANSP;
		else
			wa->regs->scc_gsmra &= ~BI_TRANSP;
	} else {
		/* clear event bits before clearing the mask */
		wa->regs->scc_scce = BISYNC_RCHR;
		/* not an STX or SOH frame */
		wa->regs->scc_sccm |= BISYNC_RCHR;
		issue_cmd(ENTER_HUNT_MODE | (channel << 6), wa->quicc);
		bd->status |= R_C;
		return;
	}

	/*
	 * wait for next character to be received
	 * this section is only reached for STX or SOH frames
	 */
	j = 0;
	while( bd->length < (tr+2) ) j++;
	if( j > 1000 )
		return;

	wa->regs->scc_gsmra |= BI_BCS;
	wa->regs->scc_sccm &= ~BISYNC_RCHR;
}

bisync_busy()
{
}
