
/*
 * hostmem.c: version 1.1 of 6/4/85
 *
 *  hostmem.c -- Handlers for Host Memory device
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg	November, 1984
 */

#include "vinc/viosconf.h"
#include "vinc/iopacket.h"
#include "vinc/viocmds.h"
#include "vinc/viostatus.h"
#include "vinc/piostatus.h"
#include "vinc/handlers.h"
#include "vinc/pktfuncs.h"
#include "vinc/poolfuncs.h"
#include "vinc/ascii.h"
#include "vinc/hostflags.h"
#include "vinc/hostsubrs.h"

#include "INC5/3200config.h"

int read_host();
int write_host();

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
#define HOST_WSIZE (END_HOST_WINDOW - HOST_WINDOW)
#endif /************************* CPU REVISION-2A **********************/

    PKT_STATE					/* Throw: Errors from drivers */
hmem_dispatcher (cmd, dev, pkt)
    int cmd;
    PDD_PTR dev;
    PKT_PTR pkt;

/* hmem_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 Host Memory devices (devices whose physical names
 *	begin with the string "AIS:HOSTMEM").  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.
 *
 *		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(DONE_STATE);	/* nothing to do for device close */

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

	switch (Ftype(*pkt))
	    {
	case WRITE_FUNCTION:
	    return(hmem_write(dev,pkt));

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

#ifdef DEBUG3   /*************************************************************/
	case DEVICE_FUNCTION:

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

	    } /*switch - Ftype*/

#ifdef DEBUG_MAP   /***********************************************************/
    case PDMAPPOOL:
	return(0);
#endif /* DEBUG_MAP ***********************************************************/

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

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

/* hmem_create (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 (DONE_STATE)
 *	thrown:	V_NO_DEVICE	thrown by initty, if device does not exist
 *
 *	Fill in the Physical Unit Descriptor (PUD) for a Host Memory buffer,
 *	specified by the device name in 'dev'.
 *	Set the Host Memory address to Parameter Two and the buffer size,
 *	in bytes, to Parameter Three.
 *
 *	Dispatched through ais_dispatcher() (in aisdispat.c).
 */
{
    register PDD *dv;
    register IO_PACKET *pk;

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

    pk = *pkt;

    if ((Bufa_size(dv) = Param3(pk)) EQ 0)	/* get buffer size */
	throw (V_BAD_PARAMS);
    
    Bfp_type(dv) = HOST_MEMORY;			/* set type of device */
    Devcsr(dv) = Param2(pk);			/* set start address */


    /* *** NOTE *** Verify Host Memory Address here *** */


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

    return(DONE_STATE);
}

    PKT_STATE
hmem_write (dev, pkt)
    PDD_PTR dev;
    PKT_PTR pkt;

/* hmem_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 Host Memory.
 */
{
    register IO_PACKET *pk;

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

    /* Copy from user buffer to Host Memory, offset by Param1 */

    cp_from_buffer(&Ubuffer(pk), (Param1(pk) + Devcsr(*dev)), 0,
						    Ubufsize(pk), write_host);

    Statcode(pk) = V_SUCCESS;
    Iocount(pk)--;		/* Count down the i/o count */
    return(DONE_STATE);		/* Pass packet back to be requeued */
}

    PKT_STATE
hmem_read (dev, pkt)
    PDD_PTR dev;
    PKT_PTR pkt;

/* hmem_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 from Host Memory.
 */
{
    register IO_PACKET *pk;

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

    /* Copy from Host Memory, offset by Param1, to user buffer */

    cp_to_buffer((Param1(pk) + Devcsr(*dev)), &Ubuffer(pk), 0,
						    Ubufsize(pk), read_host);

    Statcode(pk) = V_SUCCESS;
    Iocount(pk)--;		/* Count down the i/o count */
    return(DONE_STATE);		/* Pass packet back to be requeued */
}


read_host (from, to, bcnt)
    char *from;
    char *to;
    int bcnt;

/* read_host (from, to, bcnt) -- Copy from host to local memory
 *	in:	from		Address of source (host memory address)
 *	in:	to		Address of destination
 *	in:	bcnt		Number of bytes to copy
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Copy 'bcnt' bytes from host memory at 'from' to local memory at 'to'.
 *	The host memory address window is set to map the appropriate section
 *	of memory, and the source address is adjusted to map through the
 *	AIS I/O Page Host Window.  Blk_from() is called to perform the actual
 *	transfer.
 *
 *	If the host is not connected, this routine becomes a no-op, with no
 *	error indication returned.
 */
{
#ifdef REV_2A /****************** CPU REVISION-2A **********************/
    register unsigned hstart;
    register int cnt;

    while ( (bcnt NE 0) AND Host_connected )
	{
	DIS_INTS;		/* disable ints...host interrupt calls this */

	hstart = ((unsigned) from) & (HOST_WSIZE - 1);	/* offset into window */

	/* Set high order bits for Host-Bus mapping */
	*((UB8*)HOST_MAP) = (UB8) (((unsigned) from) >> 16);

	cnt = HOST_WSIZE - hstart;		/* bytes left in window */

	if (bcnt LT cnt)
	    cnt = bcnt;			/* not accessing whole window */

	blk_from ( (hstart | HOST_WINDOW), to, cnt);	/* copy data */

	bcnt -= cnt;			/* reduce remaining count */
	from += cnt;			/* increment source address */
	to += cnt;			/* increment dest address */

	ENA_INTS;		/* let interrupts in for a usec */
	}
#endif /************************* CPU REVISION-2A **********************/

}


write_host (from, to, bcnt)
    char *from;
    char *to;
    int bcnt;

/* write_host (from, to, bcnt) -- Copy from local to host memory
 *	in:	from		Address of source
 *	in:	to		Address of destination (host memory address)
 *	in:	bcnt		Number of bytes to copy
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Copy 'bcnt' bytes from local memory at 'from' to host memory at 'to'.
 *	The host memory address window is set to map the appropriate section
 *	of memory, and the destination address is adjusted to map through the
 *	AIS I/O Page Host Window.  Blk_to() is called to perform the actual
 *	transfer.
 *
 *	If the host is not connected, this routine becomes a no-op, with no
 *	error indication returned.
 */
{
#ifdef REV_2A /****************** CPU REVISION-2A **********************/
    register unsigned hstart;
    register int cnt;

    while ( (bcnt NE 0) AND Host_connected )
	{
	DIS_INTS;		/* disable ints...host interrupt calls this */

	hstart = ((unsigned) to) & (HOST_WSIZE - 1);	/* offset into window */

	/* Set high order bits for Host-Bus mapping */
	*((UB8*)HOST_MAP) = (UB8) (((unsigned) to) >> 16);

	cnt = HOST_WSIZE - hstart;		/* bytes left in window */

	if (bcnt LT cnt)
	    cnt = bcnt;			/* not accessing whole window */

	blk_to (from, (hstart | HOST_WINDOW), cnt);	/* copy data */

	bcnt -= cnt;			/* reduce remaining count */
	from += cnt;			/* increment source address */
	to += cnt;			/* increment start address */

	ENA_INTS;		/* let interrupts in for a usec */
	}
#endif /************************* CPU REVISION-2A **********************/

}
