/*********************************************************************** 
* 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:	hdlc_t.c
 *
 * Description:
 *	The driver routines for handling
 *	transmission on an HDLC port.
 *
 * Routines:
 *	hdlc_txreq
 *	hdlc_conf
 *	hdlc_txerr
 *	hdlc_stoptx
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/


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




/********************************************************
 * routine:	hdlc_txreq
 *
 * description:
 *	Request for transmission of an HDLC channel.
 *	T_FRAMEs are already linked via the driver's
 *	nextf pointer. It holds them in a linked list,
 *	linking them to the QUICC's BD table as slots
 *	become available.
 *
 * arguments:
 *	t		points to first T_FRAME for tx
 *
 * return code:
 *
 * side effects:
 *	Handles transmit loopbacks where necessary
 *
 ********************************************************/

hdlc_txreq(t, child)
register T_FRAME *t;
CHILD *child;
{
	register WORK_AREA *wa;
	R_FRAME *r;
	extern R_FRAME *tx_to_rx();

	wa = (WORK_AREA *)child->work_area;
	if( !wa->req_q )
		wa->tx_q = wa->conf_q = t;
	else {
		wa->req_q->layer[0].nextf = t;
		if( !wa->tx_q )
			wa->tx_q = t;
	}

	/*
	 * find new end of queue
	 */
	while( t->layer[0].nextf )
		t = t->layer[0].nextf;
	wa->req_q = t;

	/*
	 * if not loopback, call transmit_frames,
	 * else call loopback
	 */
 	if( !(child->flags & LOOP_MODE) )
		transmit_frames(child);
 	else {
		/*
		 * the child id that is looped-back to
		 * is written in this child's lower
		 */
		GCT *gct;
		int lower;
 
		GETGCT(gct);
		lower = child->lower;
 		child = &(gct->driver->child[lower]);
  		r = tx_to_rx(t, L1);
		if( r ) {
			r->hdr.id = child->key;	/* DORIT */
			if( (*gct->send)(child->upper, r) )
				relm((HDR *)r);
		}
		/* immediate confirm */
		wa->tx_q = t->layer[0].nextf;
		wa->conf_q = t->layer[0].nextf;
		if( !wa->conf_q )
			wa->req_q = 0;
 		conf_frm(t, child->upper);
 	}
}



/********************************************************
 * routine:	hdlc_conf
 *
 * description:
 *	Confirm transmitted frames. This routine
 *	is reached as result of a TX_CONF (primitive)
 *	message sent to the driver module by the
 *	SCC interrupt routine handler.
 *	It implies that transmission of an HDLC
 *	frame has been completed.
 *	Note that transmit_frames is called to possibly link
 *	new frames to the BD entries that have been
 *	released.
 *	The 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		the child structure involved
 *
 * return code:
 *
 * side effects:
 *	Interrupts for TXBD should be re-enabled (they
 *	were disabled in the interrupt routine).
 *
 ********************************************************/
hdlc_conf(h, child)
HDR *h;
CHILD *child;
{
	register WORK_AREA *wa;
	register int i = 0;
	T_FRAME *t;
	extern T_FRAME *confirm_frame();

	wa = (WORK_AREA *)child->work_area;

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

	/*
	 * if confirm is disabled,
	 * return immediately
	 */
	if( child->flags & CONF_DISABLE )
		if( child->flags & CONF_ACTIVATE )
			child->flags &= ~CONF_ACTIVATE;
		else
			return;

	while( t = confirm_frame(wa) ){
		i++;
		driver_trace(TX_CONF, t->hdr.id, t);
		conf_frm(t, child->upper);
	}

	/*
	 * re-enable interrupts before calling transmit_frames!!
	 */
	wa->regs->scc_sccm |= HDLC_TBD;

	if( i )
		transmit_frames(child);
}


/********************************************************
 * routine:	hdlc_txerr
 *
 * description:
 *	Called after receiving an HDLC_TXE interrupt.
 *	This can result from either CTS lost or
 *	transmit underrun.
 *	The 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		the child structure concerned
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
hdlc_txerr(h, child)
HDR *h;
CHILD *child;
{
	WORK_AREA *wa;

	wa = (WORK_AREA *)child->work_area;

	/*
	 * clean up after error
	 */
	transmit_error(wa);

	/*
	 * confirm all transmitted frames
	 */
	hdlc_conf(h, child);
}

/********************************************************
 * routine:	hdlc_abort
 *
 * description:
 *	Sent by an upper layer to immediately
 *	stop transmitting on this channel
 *	(if a buffer is currently being tx-ed,
 *	an abort frame will be generated).
 *	The STOP_TX command is issued.
 *
 * arguments:
 *	h		the event message received
 *	child		the child structure concerned
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
hdlc_abort(h, child )
HDR *h;
CHILD *child;
{
	WORK_AREA *wa;

	wa =(WORK_AREA *)child->work_area;
	issue_cmd(STOP_TX + (wa->channel << 6), wa->quicc);
	child->flags |= TX_STOPPED;
	relm(h);
}
