h32899
s 00206/00056/00728
d D 3.2 85/02/13 00:24:37 dan 2 1
c Added DTR handling / corrected BREAK and misc. minor bugs
e
s 00784/00000/00000
d D 3.1 84/11/13 16:26:41 dan 1 0
c date and time created 84/11/13 16:26:41 by dan
e
u
U
t
T
I 1
/*  AISTTY.C -- Terminal handlers for on-board AIS UARTs
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg
 *	November, 1984
 *
 */

#include "viosconf.h"
#include "iopacket.h"
#include "viocmds.h"
#include "viostatus.h"
#include "piostatus.h"
#include "handlers.h"
#include "pktfuncs.h"
#include "poolfuncs.h"
#include "ascii.h"

#include "3200config.h"


#define IBUFSIZE (80)	/* Default record and input buf size for terminals */
#define MAXBUFSIZE (256)	/* Maximum record/buffer width */


static BOOLEAN out_chr();	/* define character output routine */
static BOOLEAN accept_char();	/* define character acceptance routine */

I 2
    PKT_STATE					/* Throw: Errors from drivers */
tty_dispatcher (cmd, dev, pkt)
    int cmd;
    PDD_PTR dev;
    PKT_PTR pkt;

/* tty_dispatcher (cmd, dev, pkt)
 *	in:	cmd		dispatcher function (PDREMOVE / STARTIO / etc)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet
 *	return:	(PKT_STATE)	current state of i/o packet
 *	thrown:	(?)		(Drivers may throw errors...see note 1 below)
 *
 *	Dispatcher for AIS local terminals (devices whose physical names
 *	begin with the string "AIS:TTY").  Typically, this dispatcher will
 *	be called for device removal (PDREMOVE) without an active i/o packet,
 *	since device removal typically occurs at i/o completion.
 *
 *	The following should be noted concerning device dispatchers:
 *		1) 'pkt' is assumed to be the i/o packet that is currently
 *		   being dispatched through main_dispatcher() (in viomain.c).
 *		   If this is not the case, if an error condition is thrown,
 *		   it must be caught upstream before
 *		   it reaches main_dispatcher() if it could terminate the
 *		   wrong i/o packet.
 *
 *		2) Device dispatchers are typically called by q2device()
 *		   (in rdwrtsubs.c) which is mostly called by the class
 *		   dispatching modules (blkaddr.c, recseq.c, etc.).
 *		   Those routines should be referred to for more specifics
 *		   on the conditions under which the dispatcher is requested
 *		   when the 'cmd' is STARTIO.
 *		   Q2device() calls the dispatcher with interrupts disabled!
 *
 *		3) A STARTIO request will not be dispatched if the device
 *		   has its current packet non-NULL (Devipkt / Devopkt | Devpkt).
 *		   Therefore, single-request devices must ensure that the
 *		   current packet pointer slot is maintained correctly;
 *		   multi-request devices (e.g. host disk devices) must keep
 *		   that slot (or slots, if full-duplex) clear.
 *
 */
{
    switch (cmd)
	{
    case PDREMOVE:
	return(tty_remove(dev,pkt));

    case STARTIO:
#ifdef DEBUG3   /*************************************************************/
	if (Devclass(*dev) NE REC_SEQ)
	    error ("Bad device class in tty_dispatcher: %d", Devclass(*dev));
#endif /* DEBUG3 *************************************************************/

	switch (Ftype(*pkt))
	    {
	case DEVICE_FUNCTION:
	    return(tty_control(dev,pkt));

	case WRITE_FUNCTION:
	    return(tty_write(dev,pkt));

	case READ_FUNCTION:
	    return(tty_read(dev,pkt));

#ifdef DEBUG3   /*************************************************************/
	default:
	    error ("Bad i/o function in tty_dispatcher: %d", Function(*pkt));
#endif /* DEBUG3 *************************************************************/

	    } /*switch - Ftype*/

#ifdef DEBUG_MAP   /***********************************************************/
    case PDMAPPOOL:
	return(tty_mappool(dev,pkt));
#endif /* DEBUG_MAP ***********************************************************/

#ifdef DEBUG3   /*************************************************************/
    default:
	error ("Bad cmd to tty_dispatcher: %d", cmd);
#endif /* DEBUG3 *************************************************************/

	} /*switch - cmd*/
}

E 2
    PKT_STATE					/* throws: V_NO_DEVICE */
tty_create (dev, pkt)
    PDD_PTR dev;
    PKT_PTR pkt;

/* tty_create (dev, pkt)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet (usually NULL)
 *	return:	(PKT_STATE)	current state of i/o packet (DONE_STATE)
 *	thrown:	V_NO_DEVICE	thrown by initty, if device does not exist
 *
 *	Fill in the Physical Unit Descriptor (PUD) for an on-board UART,
 *	specified by the device name in 'dev'.
 *	Set the initial record length and input buffer size to Parameter
 *	one, or, if zero, IBUFSIZE.
 *
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
 */
{
    register PDD *dv;
    register IO_PACKET *pk;
    register INP_BUFFER *ibuf;

    if (Devclass(dv = *dev) NE REC_SEQ)	/* check class */
	throw(V_CLASS_MISMATCH);

    if (Param2(pk = *pkt) EQ 0)  Param2(pk) = IBUFSIZE;	/* set default width */
    else
	if (Param2(pk) GT MAXBUFSIZE)  throw (V_BAD_PARAMS);


    /* Set csrs of UARTs, depending on name ... initty() may throw an error */

    if (catch() EQ 0)
	{
	DIS_INTS;	/* Disable interrupts */

	Fullduplex(dv) = TRUE;	/* ttys are full-duplex */
	Recs_echook(dv) = TRUE;	/* ttys can echo */
	Recs_rlength(dv) = Param2(pk);	/* default buffer width */
	Recs_imode(dv) = R_STREAM;		/* records are stream mode */

	/* Initialize the physical unit descriptor */

	Rp_ostopc(dv) = CTRL_S;
	Rp_istopc(dv) = CTRL_S;	/* set stop character */
	Rp_ostartc(dv) = CTRL_Q;
	Rp_istartc(dv) = CTRL_Q;	/* and restart chars */
	Rp_ieofc(dv) = '\0';			/* and EOF character */
	Rp_othrottle(dv) = XOFFXON;
	Rp_ithrottle(dv) = XONXOFF;
	Rp_istopped(dv) = FALSE;
	Rp_itoggle(dv) = FALSE;
	Rp_ostopped(dv) = FALSE;
	Rp_ibrkpnd(dv) = FALSE;
	Rp_ibaud(dv) = U_9600;
	Rp_obaud(dv) = U_9600;

	/* Allocate and initialize an input buffer */

	ibuf = *(Rp_ibuffer(*dev) = 
	    (INP_BUFFER **) valloc(Param2(pk) + sizeof(INP_BUFFER) - 1));

	dv = *dev;				/* Refresh the device ptr */

	Ibufsize(ibuf) = Param2(pk = *pkt);		/* set buffer size */
D 2
	Ibufctr(ibuf) = Ibufptr(ibuf) = 0;		/* init ptr and count */
E 2
I 2
	Ibufctr(ibuf) = 0;		/* init ptr and count */
	Ibufptr(ibuf) = 0;
E 2


	/* Now set up the terminal uart */

	if (catch() EQ 0)
	    {
D 2
	    Devcsr(dv) = initty(dev, ((*Devname(dv)) + strlen(AISSTR)));
E 2
I 2
	    Devcsr(dv) = initty(dev, ((*Devname(dv)) + AISSTRL + UARTSTRL));
E 2
	    uncatch();
	    }
	else
	    {
	    vfree(Rp_ibuffer(dv));	/* release input buffer if error */
	    rethrow();
	    }

	tty_setbaud (Devcsr(dv), U_9600, U_9600);

	Statcode(pk) = V_SUCCESS;		/* successful completion */

	uncatch();
	ENA_INTS;			/* enable interrupts */
	return(DONE_STATE);
	} /*if-catch*/
    
    else
	{
	ENA_INTS;			/* re-enable interrupts */
	rethrow();			/* and pass the error back */
	}
}

    PKT_STATE
tty_remove (dev, pkt)
    PDD_PTR dev;
D 2
    register PKT_PTR pkt;
E 2
I 2
    PKT_PTR pkt;
E 2

/* tty_remove (dev, pkt)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet (usually NULL)
 *	return:	(PKT_STATE)	current state of i/o packet (DONE_STATE)
 *	thrown:	(NONE)
 *
 *	Remove is almost a no-op function for on-board devices.
 *	It does, however, deallocate the input buffer, if any.
 *
D 2
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
E 2
I 2
 *	Dispatched through tty_dispatcher().
E 2
 */
{
    if (chk_pool(Rp_ibuffer(*dev)))  vfree(Rp_ibuffer(*dev));
    return (DONE_STATE);
}

    PKT_STATE
tty_write (dev, pkt)
D 2
    register PDD_PTR dev;
    register PKT_PTR pkt;
E 2
I 2
    PDD_PTR dev;
    PKT_PTR pkt;
E 2

/* tty_write (dev, pkt)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet
 *	return:	(PKT_STATE)	current state of i/o packet
 *	thrown:	(NONE)
 *
 *	Initiate a device write to an on-board UART specified by 'dev'.
 *	'pkt' must have a non-zero buffer size (this is screened by
 *	vwrt_handler()).
 *
D 2
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
E 2
I 2
 *	Dispatched through tty_dispatcher().
E 2
 */
{
    Devopkt(*dev) = pkt;	/* set current output packet */
    tty_ochr(dev);		/* start output to device */
    return(DONE_WAIT_STATE);
}

    PKT_STATE
tty_read (dev, pkt)
D 2
    register PDD_PTR dev;
    register PKT_PTR pkt;
E 2
I 2
    PDD_PTR dev;
    PKT_PTR pkt;
E 2

/* tty_read (dev, pkt)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet
 *	return:	(PKT_STATE)	current state of i/o packet
 *	thrown:	(NONE)
 *
 *	Initiate a device read to an on-board UART specified by 'dev'
 *	after first flushing any input stored in the input buffer.
 *	'pkt' must have a non-zero buffer size (this is screened by
 *	vrd_handler()).
I 2
 *
 *	If the BREAK pending flag (Rp_ibrkpnd) is set, complete the
 *	i/o request immediately with a count of zero, V_SUCCESS status,
 *	and V_BREAK auxilliary status.
 *
E 2
 *	If the input buffer had filled beyond 3/4 full, restart input
 *	(if it had been stopped) if it gets 1/2 emtpy.
 *	If the input request must be echoed, and the request is satisfied
 *	from the input buffer, leave it active and it will be finished upon
 *	the final output interrupt.
 *
D 2
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
E 2
I 2
 *	Dispatched through tty_dispatcher().
E 2
 */
{
    register PDD *dv;
    register INP_BUFFER *ibuf;
    register unsigned i;
    register IO_PACKET *pk;

    pk = *pkt;		/* Dereference pkt ptr */

I 2
    /* If BREAK pending, return immediately */
    if (Rp_ibrkpnd(dv = *dev))
	{
	Rp_ibrkpnd(dv) = FALSE;		/* clear pending flag */
	Statcode(pk) = V_SUCCESS;	/* set primary status (count = 0) */
	Statauxcode(pk) = V_BREAK;	/* set aux status */
	goto Doneread;			/* finish this request now */
	}

E 2
    /* Point to input buffer and loop while any characters are in it */
D 2
    ibuf = *Rp_ibuffer(dv = *dev);
E 2
I 2
    ibuf = *Rp_ibuffer(dv);
E 2

    if (Rp_ioverrun(dv))		/* if the input buffer overflowed */
	{
	Statauxcode(pk) = V_DATA_OVERRUN;	/* set aux error code */
	Rp_ioverrun(dv) = FALSE;			/* reset condition */
	}

    while ((i = Ibufctr(ibuf)) NE 0)
	{
	if ( Rp_istopped(dv) AND (i LE (Ibufsize(ibuf)/2)) )
	    {
	    switch (Rp_ithrottle(dv))
		{
	    case XONXOFF:
		qitoggle (Rp_istartc(dv), dev);		/* restart input */
		break;

	    case DEVBUSY:
		break;
		}

	    Rp_istopped(dv) = FALSE;
	    }

	/* Accept_char() handles echo, etc. and returns TRUE if i/o complete */

	if (accept_char(ring_rem(Rp_ibuffer(dv)), dev, pkt, TRUE))
	    {
	    goto Doneread;		/* If done, finish this request */
	    }
	if (Statcode(pk) NE V_PENDING)	/* If request full (not yet echoed) */
	    {
#ifdef DEBUG3   /*************************************************************/
	    if (Fmod(pk) NE ECHO_RECORDMODE)
		error("Inconsistent packet status at %x in tty_read()", pkt);
#endif /* DEBUG3 *************************************************************/
	    break;		/* break out of while loop */
	    }

	}  /*while*/

    if ( (Fmod(pk) EQ RAWMODE) AND (Statbcount(pk) NE 0) )
	{
	Statcode(pk) = V_SUCCESS;	/* If any chars, finish raw mode */
	goto Doneread;		
	}

    
    /* At this point, the input buffer is empty and more chars are required */
    /* ...or maybe an echoed input request must be held for echo completion */

    Devipkt(*dev) = pkt;	/* More input required so set current packet */
    return(DONE_WAIT_STATE);	/* and queue it to the wait queue */


    /* Execute below if the input buffer alone satisfied the i/o request */
Doneread:
    Iocount(pk)--;		/* Count down the i/o count */
    pq_activate(PINP_WAIT_STATE, 1, dev);	/* start next packet */
    return(DONE_STATE);		/* Pass packet back to be requeued */
}

    PKT_STATE
tty_control (dev, pkt)
D 2
    register PDD_PTR dev;
    register PKT_PTR pkt;
E 2
I 2
    PDD_PTR dev;
    PKT_PTR pkt;
E 2

/* tty_control (dev, pkt)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	pkt		bufptr to active i/o packet
 *	return:	(PKT_STATE)	current state of i/o packet
 *	thrown:	(NONE)
 *
 *	Initiate a device control function for an on-board UART ('dev').
 *
D 2
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
E 2
I 2
 *	Dispatched through tty_dispatcher().
E 2
 */
{
    register PDD *dv;
I 2
    register IO_PACKET *pk;
E 2

    dv = *dev;
D 2
    switch (Fmod(*pkt))			/* dispatch on control type */
E 2
I 2
    switch (Fmod(pk = *pkt))			/* dispatch on control type */
E 2
	{
    case INP_BAUD:
    case OUT_BAUD:		/* set input/output baud rate */
	tty_setbaud(Devcsr(dv), Rp_ibaud(dv), Rp_obaud(dv));
	break;

    case INP_THROTTLE:
	Rp_itoggle(dv) = FALSE;	/* clear input restart flag */
	Rp_istopped(dv) = FALSE;	/* clear input stopped flag */
	break;

    case OUT_THROTTLE:
	Rp_ostopped(dv) = FALSE;	/* clear output stopped flag */
	tty_ochr(dev);			/* restart stopped output, if any */
	break;

I 2
    case DTR_ON:
    case DTR_OFF:
	tty_dtr(Devcsr(dv), Rp_dtr(dv));	/* turn DTR on or off */
	break;

E 2
    case INP_BUF:
    default:			/* all other controls are NOPs */
	break;
	} /*switch-Fmod*/

D 2
    Iocount(*pkt)--;		/* no more i/o pending on this pkt */
    Statcode(*pkt) = V_SUCCESS;
E 2
I 2
    Iocount(pk)--;		/* no more i/o pending on this pkt */
    Statcode(pk) = V_SUCCESS;
E 2
    return(DONE_STATE);
}

#ifdef DEBUG_MAP   /***********************************************************/

    PKT_STATE
tty_mappool (dev, map)
D 2
    register PDD_PTR dev;
    register char* map;
E 2
I 2
    PDD_PTR dev;
    char* map;
E 2

/* tty_mappool (dev, map)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	map		address of bytemap
 *	return:	(PKT_STATE)	NULL
 *	thrown:	(NONE)
 *
 *	Map pool usage for an on-board UART ('dev').
 *
D 2
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
E 2
I 2
 *	Dispatched through tty_dispatcher().
E 2
 */
{
    _map_add (map, Rp_ibuffer(*dev), 'b');	/* Map input buffer */
    return(0);
}

#endif /* DEBUG_MAP ***********************************************************/

    static		/* no return value */
tty_ochr (dev)
    PDD_PTR dev;	/* continue output on this device */

/* tty_ochr (dev)
 *	in:	dev	physical device descriptor
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Initiate the next output character on the specified device.
 *	The next character is chosen as follows:
 *		1) If an XON/XOFF character is pending (Rp_itoggle), send it.
 *		2) If there is an active input request that requires echo,
 *			send out the next character in the echo stream.
 *		   If the active input is no longer pending, and echo has
 *			been completed, finish the input request and continue.
 *		3) If there is an active output request, send the next char.
 *		   If the output request is completed, requeue the packet for
 *			completion and schedule the next output packet.
 *		4) Otherwise, do nothing.
 *
 *	Note that the highest priority waiting packets will have to go through
 *	q2device() rather than be started immediately.
 */
{
    register PKT_PTR pkt;
    register IO_PACKET *pk;
    register PDD *dv;
    char c;
    register int tmp;

    if ( (NOT Devobusy(dv = *dev)) AND (NOT Rp_ostopped(dv)) )
	{
	if (Rp_itoggle(dv))		/* If input throttle must be toggled */
	    {
	    if (Rp_istopped(dv))	/* if input must be stopped */
		c = Rp_istopc(dv);	/* XOFF */
	    else			/* if input must be restarted */
		c = Rp_istartc(dv);	/* XON */

	    if (NOT out_chr(c, dev))
		{
#ifdef DEBUG4   /*************************************************************/
		error("Output timing error 1 in tty_ochr()", NULL);
#endif /* DEBUG4 *************************************************************/
		}

	    Rp_itoggle(dv) = FALSE;	/* toggle done */
	    return;			/* output restarted....quit now */
	    }

	if ( ((pkt = Devipkt(dv)) NE NULL) AND
					(Fmod(pk = *pkt) EQ ECHO_RECORDMODE) )
	    {
	    /* There is a current input request that might require echo */

#ifdef DEBUG3   /*************************************************************/
	    if (Stataux2(pk) GT Statbcount(pk))
		error("Bad echo count (%d.) in tty_ochr()", Stataux2(pk));
#endif /* DEBUG3 *************************************************************/
	    
	    if (Stataux2(pk) LT (tmp = Statbcount(pk)))	/* echo to do */
		{
		Statbcount(pk) = Stataux2(pk);	/* set char number */
		c = get_chr(pkt);		/* get next char to echo */
		Stataux2(pk)++;			/* count a char echoed */
		Statbcount(pk) = tmp;		/* restore input counter */

		if (printable(c))
		    {
		    if (NOT out_chr(c, dev))
			{
#ifdef DEBUG4   /*************************************************************/
			error("Output timing error 2 in tty_ochr()", NULL);
#endif /* DEBUG4 *************************************************************/
			}
		    return;	/* output initiated....quit now */
		    } /*if-printable*/
		else
		    {
#ifdef DEBUG4   /*************************************************************/
    if ( (Statcode(pk) EQ V_PENDING) OR (Stataux2(pk) NE Statbcount(pk)) )
	    error("Non-printable char in record input in tty_ochr()", NULL);
#endif /* DEBUG4 *************************************************************/

		    Stataux2(pk)--;	/* uncount the non-echoed char */
		    } /*else*/
		} /*if-echo count*/

	    /* At this point, the input echo is up to date */

	    if (Statcode(pk) NE V_PENDING)	/* Input is ready to complete */
		{
		dec_iocount(pkt);		/* Return packet to Vqueue */
		Devipkt(dv) = NULL;		/* Input available */
		pq_activate(PINP_WAIT_STATE, 1, dev);	/* Start next input */
		}
	    } /*if-input request with echo*/

	if ((pkt = Devopkt(dv)) NE NULL)
	    {
	    /* If there is an active output request */
	    pk = *pkt;

	    if (Statbcount(pk) GE Ubufsize(pk))	/* if count shows i/o done */
		{
		Statcode(pk) = V_SUCCESS;
		dec_iocount(pkt);		/* Return packet to Vqueue */
		Devopkt(dv) = NULL;		/* Mark output quiescent */
		pq_activate(POUT_WAIT_STATE,1,dev);	/* get next output */
						/* pkt waiting for this dev */
		}

	    else				/* more output left to do */
		{
		if (NOT out_chr(get_chr(pkt), dev))	/* output next char */
		    {
#ifdef DEBUG4   /*************************************************************/
		    error("Output timing error 3 in tty_ochr()", NULL);
#endif /* DEBUG4 *************************************************************/
		    }
		}

	    } /*if-active output*/
	} /*if-output not stopped or busy*/
}

    static	/* no value returned */
qitoggle (chr, dev)
    int chr;			/* character to output */
    register PDD_PTR dev;	/* device to output on */

/* qitoggle (chr, dev)
 *	in:	chr		XON/XOFF character to insert in output stream
 *	in:	dev		PDD of destination device
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Toggles the input throttling bit of 'dev'.
 *	If the previous toggle of input throttling was not cleared,
 *	simply clear it.
 *	If the toggle character can be initiated now, send it out.
 *	Otherwise, set Rp_itoggle to send it out at the next opportunity.
 */
{
    if (Rp_itoggle(*dev) ^= 1)		/* toggle it by exclusive-OR */
	{
	if (out_chr(chr, dev))		/* if it got set, and output started */
	    Rp_itoggle(*dev) = FALSE;	/* clear the toggle */
	}			/* otherwise, everything's peachy */
}

    static BOOLEAN
out_chr (chr, dev)
    int chr;			/* character to output */
D 2
    register PDD_PTR dev;	/* device to output on */
E 2
I 2
    PDD_PTR dev;	/* device to output on */
E 2

/* out_chr (chr, dev)
 *	in:	chr		Character to insert in output stream
 *	in:	dev		PDD of destination device
 *	return:	(BOOLEAN)	TRUE if character queued; FALSE if device busy
 *	thrown:	(NONE)
 *
 *	If 'dev' is busy or has output stopped, returns FALSE.
 *	Otherwise, puts 'chr' on the output stream of 'dev' and returns TRUE.
 */
{
D 2
    if ( (Devobusy(*dev)) OR (Rp_ostopped(*dev)) )
E 2
I 2
    register PDD *dv;

    dv = *dev;

    if ( (Devobusy(dv)) OR (Rp_ostopped(dv)) )
E 2
	return(FALSE);		/* device was busy */

D 2
    outqio(chr, Devcsr(*dev));	/* Send character out to UART */
    Devobusy(*dev) = TRUE;	/* mark output busy and return TRUE */
E 2
I 2
    outqio(chr, Devcsr(dv));	/* Send character out to UART */
    Devobusy(dv) = TRUE;	/* mark output busy and return TRUE */
E 2
    return (TRUE);
}

    extern	/* no value returned */
out_interrupt (dev)
D 2
    register PDD_PTR dev;	/* device for which output completed */
E 2
I 2
    PDD_PTR dev;	/* device for which output completed */
E 2

/* out_interrupt (dev)
 *	in:	dev		PDD of interrupting device
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Output completion interrupt handler.
 *	Sets interrupt state, marks output complete, and attempts to
 *	initiate the next output character.
 *
 *	NOTE:
 *		This routine might be changed to work at FORK state
 */
{
    Intstate++;			/* signal interrupt state */
    Devobusy(*dev) = FALSE;	/* signal output complete */
    tty_ochr(dev);		/* attempt to start next character */
    Intstate--;			/* interrupt exit */
}

    extern		/* no value returned */
inp_interrupt (chr,dev)
    int   chr;
    PDD_PTR dev;

/* inp_interrupt (chr, dev)
 *	in:	chr		input character
 *	in:	dev		PDD of interrupting device
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Processes the input 'chr' just received on 'dev'.
I 2
 *
 *	If the input character is a BREAK, the current request is terminated
 *	immediately with a V_BREAK auxilliary status.  If there was no current
 *	request, the input buffer, if any, is flushed and Rp_ibrkpnd is set
 *	to indicate a BREAK is pending on the next Read Request.
 *
E 2
 *	If there is no current input i/o packet, the character is added to
 *	the input buffer.  If the input buffer is full, the overrun flag
 *	is set.
 *
 *	If there is an input i/o packet, the character is processed by
 *	accept_char() and added to the input request data buffer.
 *	Accept_char() also detects an input request completion.
 *
 *	NOTE:
 *		This routine might be changed to work at FORK state
 */
{
    register PDD *dv;
    PKT_PTR pkt;
    register IO_PACKET *pk;
    INP_BUFFER **bfptr;
    register INP_BUFFER *bf;

I 2
    dv = *dev;

E 2
    if (chr EQ OVERRUN)
	{
D 2
	Rp_ioverrun(*dev) = TRUE;	/* data overrun occurred...flag it */
E 2
I 2
	Rp_ioverrun(dv) = TRUE;		/* data overrun occurred...flag it */
E 2
	return;
	}

    /* Next, check to see if input character is an output throttle character */
    /* This gets a bit tricky to handle all mode/char combinations */

D 2
    if (Rp_ostopped(dv = *dev))		/* if output already stopped: */
E 2
I 2
    if (Rp_ostopped(dv))		/* if output already stopped: */
E 2
	{
	switch (Rp_othrottle(dv))
	    {					/* If character toggling, */
	case XOFFXON:				/* process, but ignore start */
	case XOFFXANY:				/* and stop characters */
	    if (chr EQ Rp_ostartc(dv))
		{
		Rp_ostopped(dv) = FALSE;	/* flag output allowed */
		Intstate++;			/* indicate interrupt state */
		tty_ochr(dev);			/* restart output */
		Intstate--;
		return;				/* Ignore the character */
		}
	    if (chr EQ Rp_ostopc(dv))
		return;				/* Ignore extra stop chars */
	    }

	if (Rp_othrottle(dv) EQ XOFFXANY)	/* If any other char restarts */
	    {
	    Rp_ostopped(dv) = FALSE;		/* flag output allowed */
	    Intstate++;				/* indicate interrupt state */
	    tty_ochr(dev);			/* restart output */
	    Intstate--;
	    }
	}  /*if-output stopped*/
    
    else					/* if output not stopped: */
	{
	switch (Rp_othrottle(dv))
	    {					/* If character toggling, */
	case XOFFXON:				/* process, but ignore start */
	case XOFFXANY:				/* and stop characters */
	    if (chr EQ Rp_ostopc(dv))
		{
		Rp_ostopped(dv) = TRUE;		/* Cancel further output */
		return;				/* Ignore the character */
		}

	    if (chr EQ Rp_ostartc(dv))		/* If this is start character */
		return;				/* ignore it */
	    }
	} /*else-output was not stopped*/


    /* Any output throttle characters (^S/^Q) have been filtered */
    /* Now, either process the input character immediately or buffer it */

    Intstate++;			/* indicate we are in interrupt state */

    /* If there is an active, uncompleted input request, add the char to it */
I 2
    /* Accept_char() knows how to handle BREAK */
E 2

    if ( ((pkt = Devipkt(dv)) NE NULL) AND (Statcode(*pkt) EQ V_PENDING) )
	{
	if (accept_char(chr, dev, pkt, FALSE))
	    {
					/* if the packet is now complete: */
	    dec_iocount(pkt);		/* requeue the packet to Vqueue */
	    Devipkt(dv) = NULL;			/* Set input quiescent */
	    pq_activate(PINP_WAIT_STATE, 1, dev);	/* start next input */
	    }  /*if-request not yet complete */

	}  /*if-input request exists*/
	    

D 2
    else if ( (bfptr = Rp_ibuffer(dv)) NE NULL )	/* if input buffer */
E 2
I 2
    else
E 2
	{
D 2
	bf = *bfptr;		/* dereference buffer ptr */
E 2
I 2
	if (chr EQ BREAK)
	    {
	    Rp_ibrkpnd(dv) = TRUE;		/* BREAK detected...flag it */
	    }
E 2

D 2
	/* If the buffer is not full, add this character */
	/* If it is a full ring-buffer, take the oldest char out first */
E 2
I 2
	if ( (bfptr = Rp_ibuffer(dv)) NE NULL )	/* if input buffer */
	    {
	    bf = *bfptr;		/* dereference buffer ptr */
E 2

D 2
	if (Ibufsize(bf) LE Ibufctr(bf))
	    Rp_ioverrun(dv) = TRUE;		/* buffer overrun */
E 2
I 2
	    /* If a BREAK was received, flush the input buffer */
	    if (chr EQ BREAK)
		{
		Ibufctr(bf) = 0;		/* no chars in buffer */
		Ibufptr(bf) = 0;		/* reset ring ptr */
		Rp_ioverrun(dv) = FALSE;	/* no overrun condition */
E 2

D 2
	if ( (Ibufsize(bf) GT Ibufctr(bf)) OR
		((Rp_ithrottle(dv) EQ RINGBUFFER) AND
		    (ring_rem(bfptr), TRUE)) )
	    {
	    ring_add(chr, bfptr);		/* add character to buffer */
E 2
I 2
		/* Restart suspended input here??? (probably not) */
E 2

D 2
	    /* If input buffer is 3/4 full, try to throttle it */
E 2
I 2
		} /*if-BREAK during buffered input*/
	
	    else			/* not a BREAK char */
		{			/* normal buffered input char */
		/* If the buffer is not full, add this character */
		/* If full ring-buffer, take the oldest char out first */
E 2

D 2
	    if ( (NOT Rp_istopped(dv)) AND
		    (Ibufctr(bf) GT ((3*Ibufsize(bf))/4)) )
		{
		switch (Rp_ithrottle(dv))
E 2
I 2
		if (Ibufsize(bf) LE Ibufctr(bf))
		    Rp_ioverrun(dv) = TRUE;		/* buffer overrun */

		if ( (Ibufsize(bf) GT Ibufctr(bf)) OR
				((Rp_ithrottle(dv) EQ RINGBUFFER) AND
				    (ring_rem(bfptr), TRUE)) )
E 2
		    {
D 2
		case XONXOFF:
		    qitoggle (Rp_istopc(dv), dev);	/* send the stop char */
		    break;
E 2
I 2
		    ring_add(chr, bfptr);	/* add character to buffer */
E 2

D 2
		case DEVBUSY:
E 2
I 2
		    /* If input buffer is 3/4 full, try to throttle it */
E 2

D 2
		    break;
		    }
		Rp_istopped(dv) = TRUE;
E 2
I 2
		    if ( (NOT Rp_istopped(dv)) AND
			    (Ibufctr(bf) GT ((3*Ibufsize(bf))/4)) )
			{
			switch (Rp_ithrottle(dv))
			    {
			case XONXOFF:
			    qitoggle (Rp_istopc(dv), dev);	/* stop char */
			    break;
E 2

D 2
		} /*if ibuf > 3/4 full*/
E 2
I 2
			case DEVBUSY:
E 2

D 2
	    } /*if room in buffer for character*/
E 2
I 2
			    break;
			    }
			Rp_istopped(dv) = TRUE;
E 2

D 2
	} /*if bufferred input*/
E 2
I 2
			} /*if ibuf > 3/4 full*/
E 2

I 2
		    } /*if room in buffer for character*/

		} /*if-not a BREAK to put in buffer*/

	    } /*if-bufferred input*/
	} /*else-no input request*/

E 2
	Intstate--;			/* no more interrupt state in VIOS */
}

    static BOOLEAN
accept_char (chr, dev, pkt, bufread)
    int chr;
    PDD_PTR dev;
    PKT_PTR pkt;
    BOOLEAN bufread;

/* accept_char (chr, dev, pkt, bufread)
 *	in:	chr		input character
 *	in:	dev		PDD of interrupting device
 *	in:	pkt		Currently active input i/o packet
 *	in:	bufread		TRUE if reading from input buffer,
 *				FALSE if input character interrupt
 *	return:	(BOOLEAN)	TRUE if input is complete / else FALSE
 *	thrown:	(NONE)
 *
 *	Process the character 'chr', received on 'dev', as an input
 *	char for the active i/o request specified by 'pkt'.
 *	This routine may be called either when the character is received
 *	or when the character is removed from the input buffer.
 *
D 2
 *	Accept_char() handles device echo, input request modes, and
E 2
I 2
 *	Accept_char() handles device echo, input request modes, BREAK and
E 2
 *	EOF special characters (it assumes output throttling characters
 *	have already been filtered and does not attempt to recognize them).
 *
 *	The status code of the packet is set if this character completes
 *	the input request, and TRUE is returned.  Note that this is
 *	correct if reading from the input buffer as long as 'bufread' is TRUE.
 *	Input buffer overrun is not dealt with at this level.
 */
{
    register IO_PACKET *pk;
    register PDD *pd;
    register BOOLEAN inpfull;

    		/* check for EOF/EOT here */


    pk = *pkt;			/* Dereference pkt ptr */
I 2

    if (chr EQ BREAK)
	{
	Statauxcode(pk) = V_BREAK;	/* aux stat shows BREAK */
	goto Retsuccess;		/* return V_SUCCESS */
	}

E 2
    put_chr (chr,pkt);		/* Add char to output buffer */

    inpfull = (Statbcount(pk) EQ Ubufsize(pk));	/* True if i/o buffer full */

    switch (Fmod(pk))		/* Depending input request mode: */
	{
    case BLOCKMODE:
	if (NOT inpfull)		/* If count low, */
	    break;			/* not done yet */
	else
	    {
D 2
	    Statcode(pk) = V_SUCCESS;		/* successful completion */
	    return(TRUE);
E 2
I 2
	    goto Retsuccess;		/* return V_SUCCESS */
E 2
	    }

    case ECHO_RECORDMODE:
	if (printable(chr))	/* if printable, echo character */
	    {
	    if (out_chr (chr, dev))	/* if echo initiated */
		{
		Stataux2(pk)++;		/* count the character echoed */

#ifdef DEBUG3   /*************************************************************/
    if (Stataux2(pk) NE Statbcount(pk))
	error("Invalid echo count %d. in accept_char()", Stataux2(pk));
#endif /* DEBUG3 *************************************************************/

		} /*if-output initiated*/

	    if (inpfull)	/* input request complete due to full buffer */
		Statcode(pk) = V_SUCCESS;	/* set status */

	    		/* return FALSE and let tty_ochr complete it */
	    } /*if-printable*/

	else			/* character non-printable */
	    {
	    Statcode(pk) = V_SUCCESS;		/* input is now complete */
	    if (Devobusy(*dev))
		break;		/* request will finish at end of echo */
	    if ( (Stataux2(pk)+1) EQ Statbcount(pk) )
		return(TRUE);	/* request is complete and fully echoed */

#ifdef DEBUG3   /*************************************************************/
	if (NOT Rp_ostopped(*dev))
	    error("Input echo stream broken at accept_char()", NULL);
#endif /* DEBUG3 *************************************************************/

	    			/* request echo is suspended */
	    } /*else-non-printable*/

	break;    		/* return FALSE and let tty_ochr complete it */

    case RECORDMODE:
	if ( (NOT inpfull) AND printable(chr) )
	    break;			/* if printable, continue collecting */

    case RAWMODE:
	if ( (Fmod(pk) EQ RAWMODE) AND (bufread) AND (NOT inpfull) )
	    break;
I 2

Retsuccess:
E 2
	Statcode(pk) = V_SUCCESS;		/* successful completion */
	return(TRUE);

	} /*switch--fmod*/
    
    return(FALSE);		/* i/o packet not done yet */
}
E 1
