/*********************************************************************** 
* 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:	uart_t.c
 *
 * Description:
 *	This file deals with the transmission of
 *	data over an SCC channel operating in the
 *	UART (asynchronous) mode.
 *	Note that the driver functions in two different
 *	modes of operation: a message (frame) oriented
 *	in which data is passed used the T_FRAME structure;
 *	and a character oriented mode in which the
 *	driver routines are (in the main) called directly
 *	by the module requiring transmission services,
 *	with the data character passed as an argument.
 *
 * Routines:
 *	uart_txreq	request: transmission of T_FRAME
 *	uart_conf	interrupt: a frame has been tx-ed
 *	uart_txchar	request: transmit a character
 *	uart_txbuf	interrupt: a buffer has been tx-ed
 *	uart_txchar	request: single character transmission
 *	uart_abort	request: abort transmission
 *				(destructive)
 *	uart_freeze	request: temporarily halt transmission
 *				(non-destructive)
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/


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



/********************************************************
 * routine:	uart_txreq
 *
 * description:
 *	A T_FRAME has been sent by a higher layer module
 *	for transmission on a UART port. Several T_FRAMEs
 *	may be linked via layer[0].nextf; each frame
 *	may consist of up to two buffers (one header and one
 *	data buffer).
 *
 * arguments:
 *	t		points to the T_FRAME received
 *	child		the child structure involved
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
uart_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 uart_tx,
	 * 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:	uart_conf
 *
 * description:
 *	This routine is called to after UART frames
 *	have been transmitted. It is intended for
 *	use in a message (frame) oriented environment
 *	only: for the character oriented mode, the txbuf
 *	primitive should be used instead.
 *	As with all interrupt-based messages, only a
 *	32-bit message is sent, which must not be relm-ed.
 *
 * arguments:
 *	h		the 32-bit message header received
 *	child		points to the child structure involved
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
uart_conf(h, child)
HDR *h;
CHILD *child;
{
	register WORK_AREA *wa;
	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 = UART_TX;

	/*
	 * 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 |= UART_TX;

	if( i )
		transmit_frames(child);
}

/********************************************************
 * routine:	uart_txbuf
 *
 * description:
 *	A buffer has been transmitted on a UART channel.
 *	This is called after the 32-bit dummy message
 *	has been sent from the interrupt routine.
 *	These messages should only be sent when the UART is
 *	functioning in a character oriented mode: for
 *	confirming frames in a message oriented mode,
 *	the TX_CONF message should be used instead (uart_conf).
 *
 * arguments:
 *	h		the 32-bit header pointer
 *	child		the child structure involved
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
uart_txbuf(h, child)
HDR *h;
CHILD *child;
{
	register WORK_AREA *wa;
	register QUICC_BD *bd, *nextBD;

	wa = (WORK_AREA *)child->work_area;
	nextBD = bd = wa->quicc_cbd;

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

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

	while( !(bd->status & T_R) ){
		if( bd->status & UART_T_ERROR )
			transmit_error(wa);

		if( wa->tfree == wa->tsize )
			break;

		/*
		 * advance bd (to next Tx BD in table)
		 */
		if( bd->status & R_W )
			nextBD = TBD_ADDR(wa);
		else
			nextBD++;

		wa->tfree++;
		bd = nextBD;
	}

	wa->quicc_cbd = bd;

	/*
	 * re-enable interrupts
	 */
	wa->regs->scc_sccm |= UART_TX;
}



/********************************************************
 * routine:	uart_txchar
 *
 * description:
 *	This routine is called directly (via the GCT)
 *	by any module wishing to transmit a single
 *	character on the UART channel.
 *
 * arguments:
 *	c		the 8-bit character for transmission
 *	n		the driver's child id
 *
 * return code:
 *	0		character couldn't be linked to BD table
 *	otherwise	character linked for transmission
 *
 * side effects:
 *
 ********************************************************/
uart_txchar(c, n)
unsigned char c;
int n;
{
	GCT *gct;
	WORK_AREA *wa;
	register QUICC_BD *bd;
	int i = 0;

	GETGCT(gct);
	wa = (WORK_AREA *)gct->driver->child[n].work_area;
	bd = wa->quicc_tbd;
	while( bd->status & T_R )
		if( i++ > 0x4000 )
			return;		/* table is already full */

	bd->buf[0] = c;
	bd->length = 1;

	/* advance bd */
	if( bd->status & T_W )
		wa->quicc_tbd = TBD_ADDR(wa);
	else
		wa->quicc_tbd = bd + 1;
	bd->status |= T_R;
}


/********************************************************
 * routine:	uart_abort
 *
 * description:
 *	Abort transmission on an SCC channel upon user request.
 *
 * arguments:
 *	h		pointer to the message received
 *	child		points to the child structure involved
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
uart_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);
}


/********************************************************
 * routine:	uart_freeze
 *
 * description:
 *	Set or clear the freeze bit on the SCC channel.
 *	This has the affect of either stopping and
 *	continuing transmission on an SCC - UART channel.
 *	The status field in the message header is
 *	used to freeze (status = 1)
 *	or to enable (status = 0) transmission.
 *
 * arguments:
 *	h		pointer to the message received
 *	child		points to the child structure involved
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
uart_freeze(h, child)
HDR *h;
CHILD *child;
{
	register WORK_AREA *wa;

	wa = (WORK_AREA *)child->work_area;
	if( h->status )
		wa->regs->scc_psmr |= UART_FRZ;
	else
		wa->regs->scc_psmr &= ~UART_FRZ;

	h->status = 0;
	conf_msg(h, child->upper);

}
