
/*
 * rdwrtsubs.c: version 3.2 of 6/4/85
 *
 *  rdwrtsubs.c -- VIOS Read/Write request subroutines
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg	November, 1984
 */
#ifdef SCCS
static char *sccsid = "@(#)rdwrtsubs.c	3.2";
#endif

#include "vinc/viosconf.h"
#include "vinc/iopacket.h"
#include "vinc/handlers.h"
#include "vinc/pktfuncs.h"
#include "vinc/viocmds.h"
#include "vinc/viostatus.h"


    PKT_STATE						/* throws: ??? */
q2device (pkt)
    PKT_PTR  pkt;

/* q2device (pkt) -- Queue a packet to its physical device, if not busy
 *
 *	in:	pkt		Target i/o packet
 *	return:	(PKT_STATE)	next packet state
 *	thrown:	???		Errors may be thrown from device handler
 *
 *	If the target device of 'pkt' is busy (i.e., the appropriate
 *	packet ptr of the device PDD is not NULL), return the
 *	appropriate PIO_WAIT_STATE (viomain() will put it on the physical device
 *	wait queue (Pqueue)).  The handler is set to q2device() so that
 *	the packet will be queued as soon as the device becomes available.
 *
 *	If the target device is not busy, increment the packet i/o count
 *	and call the device dispatcher with a STARTIO command.
 *
 *	The device dispatcher must either complete the packet i/o and
 *	return DONE_STATE, set a new handler and state number and
 *	return VIO_RUN_STATE, or initiate i/o and return a WAIT_STATE.
 *	If the device handler allows only one active i/o packet per
 *	device (or two, if full-duplex), it must set the device active packet
 *	ptr in the PDD, and clear it when i/o completes.
 *
 *	Q2device() returns the state returned by the device dispatcher.
 *
 *	Callers of this routine must be prepared to catch errors thrown
 *	from the device handlers.  Also, callers of this routine should
 *	refresh any pool dereferences, in case device handlers allocate pool.
 *
 *	See notes in ais_dispatcher() (in aisdispat.c) and host_dispatcher()
 *	(in hstdispat.c) for more information on device dispatching and
 *	handling.  This routine is usually called by the Class Handlers
 *	(in blkaddr.c, recseq.c, etc.).  [Also, it is called by
 *	pdevice_handler() in devhandlr.c.]
 */
{
    register IO_PACKET *pk;
    PDD_PTR pdd;
    register PDD *pd;
    register PKT_PTR q;
    register PKT_STATE state;

    pd = *(pdd = PDevice(pk = *pkt));	/* dereference packet & device ptr */

    DIS_INTS;

    if (Fullduplex(pd))
	{
	switch (Ftype(pk))
	    {
	case DEVICE_FUNCTION:
	    q = NULL;			/* Always initiate device control */
	    break;

	case WRITE_FUNCTION:
	    q = Devopkt(pd);		/* Get active output packet, if any */
	    state = POUT_WAIT_STATE;	/* Set wait state, if needed */
	    break;

	case READ_FUNCTION:
	    q = Devipkt(pd);		/* Get active input packet, if any */
	    state = PINP_WAIT_STATE;	/* Set wait state, if needed */
	    break;

#ifdef DEBUG4   /*************************************************************/
	default:
	    error ("bad function in q2device", NULL);
#endif /* DEBUG4 *************************************************************/

	    } /*switch - Ftype*/
	} /*if - fullduplex*/

    else
	{
	q = Devpkt(pd);		/* not full-duplex devices */
	state = PIO_WAIT_STATE;
	}

    /* If there is no active packet for the device, call the dispatcher */
    /* with interrupts disabled */
    /* Otherwise, set the handler to q2device() for a future retry, */
    /*       and cause the packet to wait for the physical device */
    /* Return to caller with interrupts disabled */

    return( (q EQ NULL) ?
			(Iocount(pk)++, (*Dispatcher(pd))(STARTIO, pdd, pkt)) :
			(Handler(pk) = q2device,  state) );
}


resetdev (pkt, dev)
    register PKT_PTR pkt;
    register VDD_PTR dev;

/* resetdev (pkt, dev) -- Alter the device assignment of an I/O packet
 *
 *	mod:	pkt		Target i/o packet
 *	in:	dev		New device bufptr
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Change the target device of 'pkt' to 'dev'.  Make sure that the
 *	device i/o counts are handled correctly.  If the old device's
 *	i/o count goes to zero and the device is marked for removal,
 *	it could go away at this time.
 *
 *	The same device may be reassigned, without altering the device i/o
 *	count, in order to eliminate the possibility of removing the device
 *	accidentally.
 */
{
    register VDD_PTR olddev;

    if ( (olddev = VDevice(*pkt)) NE dev )	/* save old dev assignment */
	{
	dincioc(dev);		/* increment i/o count of new device */
	VDevice(*pkt) = dev;	/* set new device assignment */
	ddecioc(olddev);	/* decrement old device i/o count */
	}
}


dincioc (dev)
    register VDD_PTR dev;

/* dincioc (dev) -- Increment the I/O count of a device descriptor
 *
 *	mod:	dev		Target virtual/physical device descriptor
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Increment the i/o count of 'dev', if not NULL.
 */
{
    if (dev NE NULL)  Outstanding_IO(*dev)++;
}


ddecioc (dev)
    register VDD_PTR dev;

/* ddecioc (dev) -- Decrement Outstanding_IO of a Virtual/Physical Device
 *
 *	mod:	dev		Target device descriptor bufptr
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Decrements the i/o count of a device descriptor.  If the device
 *	is pending removal on i/o completion, it is removed if the count
 *	goes to zero.  Note that the attachment count must have already
 *	been reduced to zero.  Calls rem_dev() (in ctrlsubs.c) to remove
 *	either a physical or virtual device descriptor.
 *
 */
{
#ifdef DEBUG4   /*************************************************************/
    if ( (dev NE NULL) AND (Outstanding_IO(*dev) EQ 0) )
	error("ddecioc: dev @ %x has zero i/o cnt", *dev);
#endif /* DEBUG4 *************************************************************/

    if ( (dev NE NULL) AND
	(--Outstanding_IO(*dev) EQ 0) AND
	    Compremove(*dev) )
		rem_dev(dev);	/* device was set to remove when i/o done */
}


ring_add (chr, buf)
    int chr;
    INP_BUFFER **buf;

/* ring_add (chr, buf) -- Add a character to a ring-buffered INP_BUFFER
 *
 *	in:	chr		Character to add
 *	mod:	buf		bufptr to input buffer header
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Add 'chr' to the end of the ring buffer 'buf'.  Note that 'buf'
 *	is a bufptr to a variable-length structure (defined in devices.h)
 *	whose elements are the buffer management variables, followed by the
 *	ring buffer itself.
 *
 *	There must be enough room for the character in the ring buffer.
 *	Otherwise, the buffer management will be corrupted.
 */
{
    register unsigned off;
    register INP_BUFFER *b;

    b = *buf;		/* Dereference input buffer pointer */

#ifdef DEBUG4   /*************************************************************/
    if (Ibufctr(b) GE Ibufsize(b))
	error ("Ring_add() called with full buffer at %x", b);
#endif /* DEBUG4 *************************************************************/

    /* Set offset to current insert position in buffer and increment count */

    if ( (off = Ibufptr(b) + Ibufctr(b)++) GE Ibufsize(b) )
	off -= Ibufsize(b);	/* Buffer wrap-around */

    *(Ibuffer(b) + off) = chr;		/* Add new character */
}

    char
ring_rem (buf)
    INP_BUFFER **buf;

/* ring_rem (buf) -- Remove oldest character from a ring-buffered INP_BUFFER
 *
 *	mod:	buf		Target ring buffer
 *	return:	(char)		Character removed
 *	thrown:	(NONE)
 *
 *	Remove the oldest character from the ring buffer described by 'buf'.
 *
 *	There must be something on the ring buffer to remove.
 *	Otherwise, the structure will be corrupted.
 */
{
    register INP_BUFFER *b;
    register unsigned char c;

    b = *buf;		/* Dereference bufptr to ring buffer */

#ifdef DEBUG4   /*************************************************************/
    if (Ibufctr(b) EQ 0)
	error("Ring_rem() called with empty buffer at %x", b);
#endif /* DEBUG4 *************************************************************/

    c = *(Ibuffer(b) + Ibufptr(b));	/* Get the oldest character */

    if ( (--Ibufctr(b) EQ 0) OR (++Ibufptr(b) EQ Ibufsize(b)) )
	Ibufptr(b) = 0;		/* Reset ptr if empty or wrap-around */

    return(c);			/* return the character */
}

    PKT_STATE
done_handler (pkt)
    register PKT_PTR pkt;

/* done_handler (pkt) -- Dummy handler to finish i/o requests
 *
 *	in:	pkt		Target i/o packet
 *	return:	(PKT_STATE)	never returns
 *	thrown:	???		Status code of packet
 *
 *	This dummy handler simply sends back a status of completion for the
 *	i/o packet.  Presumably, it returns to viomain.c, initiating the
 *	completion of the packet.
 *
 *	This could also be written as:   throw(Statcode(*pkt))
 */
{
    return(DONE_STATE);			/* send back the status code */
}
