/*  hstdriver.c -- Handlers for HOST devices
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg
 *	November, 1984
 *
 */

#include "viosconf.h"
#include "3200config.h"
#include "iopacket.h"
#include "viocmds.h"
#include "viostatus.h"
#include "handlers.h"
#include "devfuncs.h"
#include "pktfuncs.h"
#include "poolfuncs.h"
#include "ascii.h"
#include "hostsubrs.h"
#include "hostflags.h"


/* Define some routines below */
PKT_STATE	h_remove();		/* Delayed device remove handler */
PKT_STATE	rw_setup();		/* Set up for read/write handlers */
BOOLEAN		h_lock();		/* lock host data page */
PKT_STATE	h_alloc();		/* allocate from host data page */
PKT_STATE	h_dealloc();		/* deallocate from host data page */
PKT_STATE	h_cmd();		/* send physical i/o command to host */
BOOLEAN		h_snd();		/* send commands to host */
int		write_dp();		/* write to host data-page */
int		read_dp();		/* read from host data-page */

#define host_acknowledge()	h_ack(V_ACK)	/* acknlg cmds from host */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
#define HOST_WSIZE (END_HOST_WINDOW - HOST_WINDOW)
#endif /************************* CPU REVISION-2A **********************/

    PKT_STATE
host_create (pkt)
    PKT_PTR pkt;

/* host_create (pkt) -- Handler for Create PDD on host
 *	in:	pkt		bufptr to active i/o packet
 *	return: (PKT_STATE)	current state of i/o packet
 *	thrown: (NONE)
 *
 *	Processes requests to create a physical device connection
 *	to a host-owned device as follows:
 *		1) Lock the host data-page
 *		2) Allocate a host buffer to receive the device name
 *		3) Copy the device name to the host buffer
 *		4) Send an OPEN_CHANNEL command to the host
 *		5) Call the class-dependent routine to process the
 *		   status returned by the host.
 *		6) Clear Auxparam in the i/o packet so that the pdd
 *		   is not released on i/o completion.
 *		7) Add the pdd to the Physical Device List.
 *
 *	If the data-page is not locked, or the host IOP cannot allocate
 *	resources, i/o processing is suspended until the condition clears.
 *
 *	If a fatal error occurs, the device name is released to VIOS pool
 *	and the packet is terminated, automatically releasing the pdd.
 */
{
    register IO_PACKET *pk;
    register PDD_PTR dev;
    register PDD *dv;

    dv = *(dev = (PDD_PTR) (Auxparam(pk = *pkt)));	/* Set up ptrs */

    switch(Stnum(pk))			/* dispatch on processing state */
	{
    case 0:
	Stnum(pk)++;	/* If data-page not locked, come back to next state */
	if (NOT h_lock())		/* lock dp, TRUE if already locked */
	    return(HLCK_WAIT_STATE);	/* else, restart when dp is locked */

    case 1:		/* Data page is now locked */

	/* h_alloc() initiates an IOP data-page allocation, increments */
	/* Stnum, and returns HSTAT_WAIT_STATE */
	/* If the command cannot be initiated, Stnum is left alone */
	/* and HCMD_WAIT_STATE is returned. */

	return(h_alloc(pkt, Ubufsize(pk) = (strlen(*Devname(dv)+HOSTSTRL)+1)));

    case 2:		/* Host returned status from allocate command */
	if (Statcode(pk) NE V_SUCCESS)  /* if allocate failed, */
	    {
	    Stnum(pk)--;		/* retry allocation when */
	    return(HRSRC_WAIT_STATE);	/* resource available */
	    }
	
	/* Copy device name string, excluding VIOS prefix, to host d-p */
	write_dp((*Devname(dv)+HOSTSTRL), Stataux1(pk), Ubufsize(pk));
	Stnum(pk)++;			/* go on to next state */
	
    case 3:		/* Ready to issue open command to host */

	/* h_cmd() initiates the command transmission, increments Stnum, */
	/* and returns HSTAT_WAIT_STATE */
	/* If the command cannot be initiated, Stnum is left alone */
	/* and HCMD_WAIT_STATE is returned */

	return(h_cmd(pkt, FCODE(OPEN_CHANNEL,0,Fmod(pk)), 0, Stataux1(pk),
					Param1(pk), Param2(pk), Param3(pk)));

    case 4:		/* Host returned status from open command */
	if (Statcode(pk) EQ V_IOP_BUSY) /* If IOP too busy right now, */
	    {
	    Stnum(pk) = 1;		/* Set to restart packet */
	    return(HRSRC_WAIT_STATE);	/* (IOP buffer was released) */
	    }
	
	if (Statcode(pk) NE V_SUCCESS)  /* If unsuccessful open, */
	    break;			/* quit and deallocate name */
	    
	/* Save the IOP unit number */
	Devhostnum(dv) = (unsigned) Stataux1(pk);

	/* The next line locates the appropriate class-module, */
	/* (cls_search() has already verified the device class), */
	/* and calls the class-dependent handler to finish the */
	/* processing of the status returned by the host */

	(*Classdispatcher(dv)) (PDCREATE, pkt, dev);

	Auxparam(pk) = NULL;	/* Do not deallocate pdd anymore */
	q_item((Stataux2(pk) = (unsigned) dev), &Pdevices);
	h_unlock();			/* unlock host data-page */
	return(DONE_STATE);		/* Return pdd as status */

#ifdef DEBUG4	/*************************************************************/
    default:			/* catch bad Stnums */
	error("Bad Stnum in host_create()", NULL);
#endif /* DEBUG4 *************************************************************/

	} /*switch-Stnum*/

#ifdef DEBUG3	/*************************************************************/
    if ((Statcode(pk) EQ V_BAD_BUFFER) OR (Statcode(pk) EQ V_ILL_MODIFIER))
	error("IOP cmd err in host_create(): %d", Statcode(pk));
#endif /* DEBUG3 *************************************************************/

    vfree(Devname(dv));		/* On error, release VIOS copy of device name */
    h_unlock();			/* unlock host data-page */
    return(DONE_STATE);		/* io_complete() will relase pdd */
}

    PKT_STATE					/* throws: V_ALL_FAILURE */
host_remove (dev)
    PDD_PTR dev;

/* host_remove (dev)
 *	in:	dev		bufptr to physical device descriptor
 *	return: (PKT_STATE)	current state of i/o packet
 *	thrown: (V_ALL_FAILURE) failed to queue remove request
 *
 *	This routine is called when a physical device is being cleared.
 *	Therefore, there is no active i/o packet for it.
 *
 *	Process device disconnections as follows:
 *		1) If the host is not connected, just return.  The
 *		   device will be released and not reassigned if and when
 *		   the host comes back up.
 *		2) If a Close Channel command can be sent to the host
 *		   immediately, do so, and return, freeing the device locally.
 *		3) If an i/o packet can be allocated for temporary use to
 *		   queue the device removal, do so, setting h_remove() as the
 *		   handler.  Otherwise, allpkt() will throw V_ALL_FAILURE.
 *
 *	Dispatched through host_dispatcher() (in hstdispat.c).
 */
{
    register PKT_PTR pkt;
    register IO_PACKET *pk;

    if (Host_connected)
	{
	DIS_INTS;	/* synchronize, in case HCMD_WAIT_STATE returned */

	if (NOT h_snd(sizeof(VCMD) + (2*sizeof(PPARAM)), V_PIOCMD,
		FCODE(CLOSE_CHANNEL,0,0) | (Devhostnum(*dev) << 16), NULL))
	    {				/* if cant send release immediately, */
	    Handler(pk = *(pkt = allpkt())) = h_remove;	/* get a temp pkt */
	    State(pk) = HCMD_WAIT_STATE;		/* to activate later */
	    Osid(pk) = VIOS_ID;
	    Param1(pk) = Devhostnum(*dev);	/* save host device number */
	    q_wait(pkt);			/* queue for later */
	    }
	}
    return (DONE_STATE);
}

    PKT_STATE
h_remove (pkt)
    PKT_PTR pkt;

/* h_remove (pkt) -- Special handler for completing physical device removal
 *	in:	pkt		bufptr to active (temporary) i/o packet
 *	return: (PKT_STATE)	VIO_REQUEUED, to end packet processing
 *	thrown: (NONE)
 *
 *	Finishes queued Physical Device Remove commands (see host_remove()
 *	above).  Assumes that the host device number is in Param1 of the
 *	packet, and that the packet may be freed directly to pool after the
 *	cmd is dispatched.
 *
 *	If the host is not available, simply returns HCMD_WAIT_STATE to
 *	continue waiting.
 */
{
    DIS_INTS;
    if (NOT h_snd(sizeof(VCMD) + (2*sizeof(PPARAM)), V_PIOCMD,
			FCODE(CLOSE_CHANNEL,0,0) | (Param1(*pkt) << 16), NULL))
	{
	return(HCMD_WAIT_STATE);	/* keep on waiting */
	}
    else
	{
	vfree(pkt);			/* get rid of this packet */
	return(VIO_REQUEUED);		/* packet is gone */
	}
}

    PKT_STATE					/* throw: V_ALL_FAILURE */
host_write (pkt)
    register PKT_PTR pkt;

/* host_write (pkt) -- Write Physical Device handler
 *	in:	pkt		bufptr to active i/o packet
 *	return: (PKT_STATE)	current state of i/o packet
 *	thrown: V_ALL_FAILURE	could not duplicate packet
 *
 *	Perform actual Physical Device write handling:
 *		1) Duplicate packet and requeue child.
 *		2) Lock host data page.
 *		3) Issue Allocate command to host.
 *		4) If allocation failure in BLK_ADDR device request,
 *		   reduce allocation size and retry.  Otherwise,
 *		   wait for resource available.
 *		5) Copy data to write to host data-page from i/o buffer.
 *		   If this completes request, issue Write/Deallocate command
 *		   to host.  Otherwise, issue Write command to host.
 *		6) Add size of write to parent packet byte count.
 *		   If not enough data copied to BLK_ADDR device, adjust
 *		   buffer pointer and block number in child request
 *		   and issue another write to the allocated host buffer.
 *		6) If other error from host, copy to parent packet.
 *		7) When request is complete, lock data-page.
 *		   Then complete current packet (to complete parent, etc.).
 *
 *	NOTE THAT MAPPED I/O BUFFERS ARE NOT CORRECTLY HANDLED YET...
 *	DATA IS COPIED DIRECTLY FROM THE I/O PACKET BUFFER ADDRESS.
 */
{
    register IO_PACKET *pk;
    register IO_PACKET *ppk;
    register PDD_PTR dev;
    register PDD *dv;
    register int i;

    dv = *(dev = PDevice(pk = *pkt));	/* dereference ptrs */
    ppk = *Parent(pk);			/* get parent packet ptr */

    switch(Stnum(pk))			/* Dispatch on processing state */
	{
    case 0:			/* Initial processing of parent packet */
	/* Duplicate current packet and requeue child at next state number */
	return(rw_setup(pkt));

    case 1:			/* Initial processing of child pkt */
	/* Lock data page */
	Stnum(pk)++;			/* return to next state */
	if (NOT h_lock())		/* if not yet locked, */
	    return(HLCK_WAIT_STATE);	/* wait until it is */

    case 2:			/* Data page is locked */
	/* Issue allocate command with child packet buffer size */
	return(h_alloc(pkt, Ubufsize(pk)));

    case 3:			/* Allocation completed */
	switch(Statcode(pk))
	    {
	case V_SUCCESS:
	    Stnum(pk)++;	/* continue processing at next state number */
	    break;

	case V_ALL_FAILURE:
	    /* If BLK_ADDR device request, if this is the first failure */
	    /* (as determined by comparing size to parent packet size), */
	    /* if there is enough allocatable space for at least one block, */
	    /* then retry with the size reset to the size of the largest */
	    /* buffer reported available by the host */

	    Stnum(pk)--;	/* continue at previous state num anyway */

	    if ( (Devclass(dv) EQ BLK_ADDR) AND
		    (Ubufsize(ppk) EQ Ubufsize(pk)) AND
			((i = -Stataux1(pk)) GE Blka_size(dv)) )
		{
		/* Try to allocate a smaller buffer...round it to block size */

		Ubufsize(pk) = i - (i % Blka_size(dv));
		return(VIO_RUN_STATE);		/* restart processing above */
		}
	    else
		{
		Ubufsize(pk) = Ubufsize(ppk);	/* restore size from parent */
		return(HRSRC_WAIT_STATE);	/* and retry when available */
		}

#ifdef DEBUG3	/*************************************************************/
	default:
	    errpri("Alloc err in host_write(): %d", Statcode(pk));
	    Stnum(pk) = 6;		/* finish packet processing */
	    return(VIO_RUN_STATE);
#endif /* DEBUG3 *************************************************************/			
	    } /*switch-Statcode*/

    case 4:			/* Allocate succeeded, copy data to host */

	/* cp_from_buffer() maps any user-buffer and calls write_dp() */
	/* The data copy is offset by the number of bytes already transferred */

	cp_from_buffer(&Ubuffer(pk), Stataux1(pk),
				    Statbcount(ppk), Ubufsize(pk), write_dp);

	Stnum(pk)++;		/* continue at next state number */

    case 5:			/* Start/restart write rquest to host */
	/* Subtract the size to be transferred from the parent packet byte */
	/* count and, if the request size will be satisfied, deallocate the */
	/* data page buffer after writing */
	/* 'i' is set to either 0 or the write/deallocate modifier */

	i = ((Ubufsize(ppk) - Statbcount(ppk)) LE Ubufsize(pk)) ? DEALL_BUF : 0;

	return(h_cmd(pkt, FCODE(WRITE_CHANNEL, 0, Fmod(pk)|i), Devhostnum(dv),
		Stataux1(pk), Ubufsize(pk), Param1(pk), Param2(pk)));

    case 6:			/* Write request completed */
	if (Statcode(pk) EQ V_IOP_BUSY)
	    {
	    Stnum(pk)--;	/* restart at previous state number */
	    return(HRSRC_WAIT_STATE);
	    }

	/* Add the size written to the parent packet byte count and, */
	/* if the request is done, or if there was an error, finish up */
	/* 'i' is set to the number of bytes left to transfer */

	if ( ((i = Ubufsize(ppk) - (Statbcount(ppk) += Stataux2(pk))) LE 0)
	    OR
		(Statcode(pk) NE V_SUCCESS) OR (Devclass(dv) NE BLK_ADDR) )
	    {
	    Statcode(ppk) = Statcode(pk);	/* Copy status to parent pkt */
	    Statauxcode(ppk) = Statauxcode(pk);

	    if (Stataux1(pk) LT 0)	/* if buffer was deallocated */
		{
		Stnum(pk) += 3;		/* continue to unlock data-page */
		return(VIO_RUN_STATE);
		}
	    else
		Stnum(pk)++;		/* continue below to release pool */
	    } /*if-request almost finished*/
	else
	    {
#ifdef DEBUG3	/*************************************************************/
	    if (Stataux1(pk) LT 0)
		error("Buffer released too soon in host_write()", NULL);
#endif /* DEBUG3 *************************************************************/
	    
	    /* BLK_ADDR request is not yet complete...update */
	    /* block number in current packet, and start another write. */
	    /* If the number of bytes left to be written are smaller than the */
	    /* allocated buffer, shorten the length of the last request */

	    if (Ubufsize(pk) GT i)
		Ubufsize(pk) = i;	/* shorten buffer if too long */

#ifdef DEBUG3	/*************************************************************/
	if ((Stataux2(pk) % Blka_size(dv)) NE 0)
	    error("Bad block alignment at host_write: %d. bytes",Stataux2(pk));
#endif /* DEBUG3 *************************************************************/

	    Param1(pk) += (Stataux2(pk) / Blka_size(dv));   /* update blk num */
	    Stnum(pk) -= 2;	    /* return to case 4 to copy more data */
	    return(VIO_RUN_STATE);
	    } /*else*/

    case 7:		 /* write request terminated early */
	/* De-allocate the host data-page buffer */
	return(h_dealloc(pkt, Stataux1(pk)));

    case 8:			/* deallocate is complete */
	/* unlock the data-page and finish request */

#ifdef DEBUG3	/*************************************************************/
	if (Statcode(pk) NE V_SUCCESS)
	    error("Error from deallocate in host_write: %d", Statcode(pk));
#endif /* DEBUG3 *************************************************************/

	Stnum(pk)++;

    case 9:			/* request is complete */
	h_unlock();
	Iocount(pk)--;		/* reduce current packet i/o count */
	return(DONE_STATE);

#ifdef DEBUG4	/*************************************************************/
    default:
	error("Bad Stnum in host_write", NULL);
#endif /* DEBUG4 *************************************************************/

	} /*switch-Stnum*/
}

    PKT_STATE					/* throw: V_ALL_FAILURE */
host_read (pkt)
    PKT_PTR pkt;

/* host_read (pkt) -- Read Physical Device handler
 *	in:	pkt		bufptr to active i/o packet
 *	return: (PKT_STATE)	current state of i/o packet
 *	thrown: V_ALL_FAILURE	could not duplicate packet
 *
 *	Perform actual Physical Device Read handling:
 *		1) Duplicate packet and requeue child.
 *		2) Lock host data page.
 *		3) Issue Allocate/Read command to host.
 *		4) If allocation failure in BLK_ADDR device request,
 *		   reduce allocation size and retry.  Otherwise,
 *		   wait for resource available.
 *		5) Copy data read from host data-page to i/o buffer.
 *		   Add size of read to parent packet byte count.
 *		   If not enough data read on BLK_ADDR device, adjust
 *		   buffer pointer and block number in child request
 *		   and issue another read to the allocated host buffer.
 *		6) If other error from host, copy to parent packet.
 *		7) When request is complete, release host data-page
 *		   buffer and unlock data-page.  Then complete current
 *		   packet (to complete parent, etc.).
 *
 *	NOTE THAT MAPPED I/O BUFFERS ARE NOT CORRECTLY HANDLED YET...
 *	DATA IS COPIED DIRECTLY TO THE I/O PACKET BUFFER ADDRESS.
 */
{
    register IO_PACKET *pk;
    register IO_PACKET *ppk;
    register PDD_PTR dev;
    register PDD *dv;
    register int i;

    dv = *(dev = PDevice(pk = *pkt));	/* dereference ptrs */
    ppk = *Parent(pk);			/* get parent packet ptr */

    switch(Stnum(pk))			/* Dispatch on processing state */
	{
    case 0:			/* Initial processing of parent packet */
	/* Duplicate current packet and requeue child at next state number */
	return(rw_setup(pkt));

    case 1:			/* Initial processing of child pkt */
	/* Lock data page */
	Stnum(pk)++;			/* return to next state */
	if (NOT h_lock())		/* if not yet locked, */
	    return(HLCK_WAIT_STATE);	/* wait until it is */

    case 2:			/* Data page is locked */
	/* Issue allocate/read command with request modifier, buffer size, */
	/* and any request parameters */
	return(h_cmd(pkt, FCODE(READ_CHANNEL, 0, Fmod(pk)|ALLOC_BUF),
		    Devhostnum(dv), 0, Ubufsize(pk), Param1(pk), Param2(pk)));

    case 3:			/* Initial alloc/read completed */
    case 5:			/* Subsequent re-read completed */
	switch(Statcode(pk))
	    {
	case V_ALL_FAILURE:
	    /* If BLK_ADDR device request, if this is the first failure */
	    /* (as determined by comparing size to parent packet size), */
	    /* if there is enough allocatable space for at least one block, */
	    /* then retry with the size reset to the size of the largest */
	    /* buffer reported available by the host */

	    if ( (Devclass(dv) EQ BLK_ADDR) AND
		    (Ubufsize(ppk) EQ Ubufsize(pk)) AND
			((i = -Stataux1(pk)) GE Blka_size(dv)) )
		{
		/* Try to allocate a smaller buffer...round it to block size */

		Ubufsize(pk) = i - (i % Blka_size(dv));
		Stnum(pk)--;			/* restart processing above */
		return(VIO_RUN_STATE);
		}
	    else
		Ubufsize(pk) = Ubufsize(ppk);	/* restore size from parent */
						/* and fall thru below */

	case V_IOP_BUSY:
	    /* return, when not busy, to state 2 or 4 */
	    Stnum(pk)--;
	    return(HRSRC_WAIT_STATE);

#ifdef DEBUG3	/*************************************************************/
	case V_ILL_MODIFIER:
	case V_BAD_PARAMS:
	case V_NO_DEVICE:
	case V_BAD_BUFFER:
	    error("IOP command error from host_read(): %d", Statcode(pk));
#endif /* DEBUG3 *************************************************************/			
	case V_DATA_OVERRUN:
		brkvios();

	default:
	    /* cp_to_buffer() maps any user-buffer and calls read_dp() */
	    /* The data copy is offset by number of bytes already transferred */

	    if (Stataux2(pk) GT 0)	/* if any data read, copy it */
		cp_to_buffer(Stataux1(pk), &Ubuffer(pk),
					Statbcount(ppk), Stataux2(pk), read_dp);

	    /* Add the size transferred to the parent packet byte count and, */
	    /* if the request size is satisfied, or if the request failed, */
	    /* or if the device is not BLK_ADDR, the request is almost done. */
	    /* 'i' is set to the number of bytes left to read */

	    if ( ((i = Ubufsize(ppk) - (Statbcount(ppk) += Stataux2(pk))) LE 0)
		OR
		    (Statcode(pk) NE V_SUCCESS) OR (Devclass(dv) NE BLK_ADDR) )
		{
		Statcode(ppk) = Statcode(pk);	/* Copy status to parent pkt */
		Statauxcode(ppk) = Statauxcode(pk);
		Stnum(pk) = 6;		/* continue below to release pool */
		return(VIO_RUN_STATE);
		}


	    /* BLK_ADDR request is not yet complete...update */
	    /* block number in current packet, and start another read */
	    /* If the number of bytes left to be read are smaller than the */
	    /* allocated buffer, shorten the length of the last request */

	    if (Ubufsize(pk) GT i)
		Ubufsize(pk) = i;	/* shorten buffer if too long */

#ifdef DEBUG3	/*************************************************************/
	if ((Stataux2(pk) % Blka_size(dv)) NE 0)
	    error("Bad block alignment at host_read: %d. bytes", Stataux2(pk));
#endif /* DEBUG3 *************************************************************/

	    Param1(pk) += (Stataux2(pk) / Blka_size(dv));   /* update blk num */
	    break;
	    } /*switch-Statcode*/

	Stnum(pk) = 4;		    /* current state might have been 4 */
				    /* End of case 3 / case 5 processing */


    case 4:			/* start subsequent reads to allocated buffer */
	/* Stataux1 has offset, in data-page, of allocated buffer */
	return(h_cmd(pkt, FCODE(READ_CHANNEL,0,Fmod(pk)), Devhostnum(dv),
		Stataux1(pk), Ubufsize(pk), Param1(pk), Param2(pk)));

/*  case 5 is handled above */

    case 6:			/* read request is complete */
	/* De-allocate the host data-page buffer */
	if (Stataux1(pk) GE 0)			/* if there is a buffer */
	    return(h_dealloc(pkt, Stataux1(pk)));	/* release it */

	goto nobuf;		/* if no buffer, finish up below */

    case 7:			/* deallocate is complete */
	/* unlock the data-page and finish request */

#ifdef DEBUG3	/*************************************************************/
	if (Statcode(pk) NE V_SUCCESS)
	    error("Error from deallocate in host_read: %d", Statcode(pk));
#endif /* DEBUG3 *************************************************************/

nobuf:
	h_unlock();
	Iocount(pk)--;		/* reduce current packet i/o count */
	return(DONE_STATE);

#ifdef DEBUG4	/*************************************************************/
    default:
	error("Bad Stnum in host_read", NULL);
#endif /* DEBUG4 *************************************************************/

	} /*switch-Stnum*/
}

    PKT_STATE					/* thrown: V_ALL_FAILURE */
rw_setup (pkt)
    PKT_PTR pkt;

/* rw_setup (pkt) -- Setup for Physical Read/Write
 *	in:	pkt		bufptr to active i/o packet
 *	return: (PKT_STATE)	current state of 'pkt'
 *	thrown: V_ALL_FAILURE	no pool to duplicate packet
 *
 *	Duplicate the current i/o packet 'pkt' and set the parent packet
 *	to complete when the child packet is done.  Then set the child
 *	packet to become the actual request handler.  This is done so that
 *	the child request may modify the buffer sizes and pointers, yet
 *	refer to the parent to see what is left to be done.  This is useful
 *	when the i/o request must be broken up because of host allocation
 *	failures.
 *
 *	If there is no pool to duplicate the packet, V_ALL_FAILURE is thrown.
 */
{
    register IO_PACKET *pk;
    register PKT_PTR child;

    pk = *(child = duppkt(pkt));	/* duplicate current pkt */

    Iocount(pk)++;			/* inc child i/o count */
    resetdev(child, VDevice(*pkt));	/* set child device */
    State(pk) = VIO_RUN_STATE;	/* set child active */
    Stnum(pk)++;			/* inc child state number */
    q_virtual(child);		/* add it to run queue */

    Iocount(*pkt)--;		/* dec parent i/o count */
    return(DONE_WAIT_STATE);	/* and wait for child */
}

    PKT_STATE
host_control (pkt)
    PKT_PTR pkt;

/* host_control (pkt)
 *	in:	pkt		bufptr to active i/o packet
 *	return: (PKT_STATE)	current state of i/o packet
 *	thrown: (NONE)
 *
 *	Dispatched through host_dispatcher() (in hstdispat.c)
 *	or from main packet dispatcher.
 */
{
    register IO_PACKET *pk;
    register PDD_PTR dev;
    register PDD *dv;

    dv = *(dev = PDevice(pk = *pkt));	/* Set up ptrs */

    switch(Stnum(pk))			/* dispatch on processing state */
	{
    case 0:
	/* Queue the proper i/o control function to the host */
	return(h_cmd(pkt, FCODE(CONTROL_CHANNEL, 0, Fmod(pk)), Devhostnum(dv),
		Param1(pk), Param2(pk), Param3(pk), Param4(pk)));
    
    case 1:
	if (Statcode(pk) EQ V_IOP_BUSY)
	    {
	    Stnum(pk)--;		/* retry when host available */
	    return(HRSRC_WAIT_STATE);
	    }

	Iocount(pk)--;			/* reduce pkt i/o count */
	return(DONE_STATE);
	} /*switch-Stnum*/
}

#ifdef DEBUG_MAP   /***********************************************************/

    PKT_STATE
host_mappool (dev, map)
    register PDD_PTR dev;
    register char* map;

/* host_mappool (dev, map)
 *	in:	dev		bufptr to physical device descriptor
 *	in:	map		address of bytemap
 *	return: (PKT_STATE)	NULL
 *	thrown: (NONE)
 *
 *	Dispatched through host_dispatcher() (in hstdispat.c).
 */
{
    return(0);
}

#endif /* DEBUG_MAP ***********************************************************/


host_interrupt ()

/* host_interrupt () -- Handle interrupts from host
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Handler for interrupts from the host, caused by a write to the CSR.
 *
 *	Does the following:
 *		0) Reads the Host CSR.
 *		1) If there is no host connection, dismiss the interrupt
 *		   unless it is a valid host connection command.
 *		2) If this is a valid host connection, execute the host
 *		   reboot sequence (host_reboot()).
 *		3) If there is a host already connected, handle host
 *		   down commands by calling host_crash() and handle host
 *		   connection commands by calling host_crash() and then
 *		   host_reboot().
 *		4) Otherwise, 'csr' should be zero, in which case the
 *		   reason for the interrupt is found in the Host Protocol
 *		   Block.
 *		5) Host commands are processed as follows:
 *			H_ADDR - store locked data-page address and acknowledge
 *			H_PIOST- verify that the specified packet is waiting
 *					for host status, copy status to it,
 *					and activate it
 *			H_RESRC- activate all packets waiting for host resource
 *			H_VIOCM- set Host_cmdpending to start processing later
 *		6) If a VIOS command is being acknowledged, activate any i/o
 *		   that was waiting for acknowledgement.
 *		7) If the VIOS command block is available and there is a
 *		   data-page lock pending, initiate the lock.
 *		8) If the VIOS command block is still available, activate the
 *		   next packet waiting to transmit a command.
 */
{
    register PKT_PTR pkt;
    register IO_PACKET *p;
    UB16 csr;
    PROTOCOL_BLOCK prot;	/* Local copy of host protocol block */

    Intstate++;

    csr = *((UB16*) HOST_CSR);	/* read csr that was written by the host */

    if (NOT Host_connected)	/* If there is no host connection yet */
	{
	if (csr GT MAXHOSTFUNC)		/* If this is a connection */
	    host_reboot(csr);		/* mark host connected, etc. */
	else
	    ;		    /* dismiss interrupt...host is not connected */
	}
    else			/* If host is connected */
	{
	if (csr EQ 0)		/* if normal function */
	    {
	    /* Read the protocol block from the host */
	    read_host(Host_protocol, &prot, sizeof(PROTOCOL_BLOCK));

	    /* First, process commands from the IOP */
	    switch(Hcmd(prot))
		{
	    case H_DOWN:	/* host going down */
		host_crash();	/* try to save VIOS */
				/* fall thru to break below */

	    case 0:		/* no command */
	    case V_ACK:		/* acknowledge not yet cleared */
	    case V_CNCT:	/* connect ack not cleared */
		break;

	    case H_BRK:		/* break to debugger */
	    case H_RESET1:	/* Reset level 1 */
	    case H_RESET2:	/* Reset level 2 */
		brkvios();
		host_acknowledge();	/* clear the command */
		break;


	    case H_ADDR:	/* Report data page address */
		Dp_address = Hp1(prot); /* Save the data-page address */
		Dp_maxalloc = Hp2(prot); /* Size of the largest allocation */
		Dp_locked = TRUE;	/* Data-page is now locked */
		Host_lckpending = FALSE;    /* no more pending lock */
		host_acknowledge();	/* clear the command */

		/* Activate all packets waiting for a data-page lock */
		wq_activate(HLCK_WAIT_STATE, 0);
		break;

	    case H_PIOST:	/* IOP returning status */
		host_acknowledge();	/* Clear the command */

		/* Make sure the status is for a real packet */
		/* Chk_pool() returns FALSE if pkt == NULL */

		if ( (chk_pool(pkt = (PKT_PTR) Hp2(prot))) AND
					(State(*pkt) EQ HSTAT_WAIT_STATE) )
		    {
		    p = (IO_PACKET*) Wqueue.next;   /* init for search */
		    while (p NE NULL)		    /* of wait queue */
			{
			if ((PKT_PTR) p EQ pkt)	    /* if found pkt, */
			    {
			    Statcode(p = *pkt) = Hp1lo(prot); /* set status */
			    Statauxcode(p) = Hp1hi(prot);
			    Stataux1(p) = Hp3(prot);	/* set aux status */
			    Stataux2(p) = Hp4(prot);
			    Stataux3(p) = Hp5(prot);
			    Stataux4(p) = Hp6(prot);

			    State(p) = VIO_RUN_STATE;	/* turn back on */
			    q_virtual(uq_wait(pkt));	/* and requeue */
			    break;			/* get out of while */
			    } /*if-pkt found*/
			
			p = (IO_PACKET*) Next(*(PKT_PTR)p); /* try next pkt */
			} /*while*/
		    } /*if-valid pkt*/
		
		break;			/* status processed or ignored */

	    case H_VIOCM:	/* Incoming command from IOP */
		Host_cmdpending = TRUE; /* Deal with this at normal state */
		break;

	    case H_RESRC:	/* IOP says "resource available" */
		host_acknowledge();	/* clear the command */
		wq_activate(HRSRC_WAIT_STATE, 0);  /* start all waiting pkts */
		break;

#ifdef DEBUG3	/*************************************************************/
	    default:
		errpri("Bad host cmd: %d\n", Hcmd(prot));
#endif /* DEBUG3 *************************************************************/

		} /*switch-hcmd*/


	    /* Now, check the other half of the protocol block to see if */
	    /* any VIOS->IOP command has been acknowledged */

	    if (Vcmd(prot) EQ H_ACK)
		{
		Vcmd(prot) = 0;  /* clear local copy and then host copy */
		write_host(&Vcmd(prot), &(Vcmd(*Host_protocol)), sizeof(VCMD));
		wq_activate(HACK_WAIT_STATE,1); /* restart any waiting pkt */
		}


	    if (Vcmd(prot) EQ 0)	/* if host can accept a new cmd */
		{
		Host_available = TRUE;  /* set flag */
		if (Host_lckpending EQ 1)   /* if waiting to lock */
		    {
		    Host_lckpending = -1;   /* still waiting, but issued */
		    h_snd(sizeof(VCMD), V_LOCK);  /* send out lock request */
		    }
		else if (NOT Host_lckpending)	/* if not waiting to lock dp */
		    {
		    wq_activate(HCMD_WAIT_STATE, 1);	/* start a pkt */
		    }
		} /*if-vcmd clear*/

	    } /*if-csr clear*/

	else if (csr EQ H_DOWN)		/* if host going down, */
	    host_crash();		/* try to save our ass */
	    
	else if (csr GT MAXHOSTFUNC)	/* If connected host coming up */
	    {
	    host_crash();		/* crash the VIOS first */
	    host_reboot(csr);		/* then bring it back up */
	    }

#ifdef DEBUG3	/*************************************************************/
	else
	    error("Bad csr from host: %d", csr);
#endif /* DEBUG3 *************************************************************/

	} /*else-host is connected*/

    Intstate--;
}

    BOOLEAN
h_lock ()

/* h_lock () -- Lock host data-page
 *	return: (BOOLEAN)	True, if data-page already locked
 *				False, if lock is now pending
 *	thrown: (NONE)
 *
 *	If the data-page is already locked, clears any pending unlocks
 *	and returns TRUE.
 *
 *	Otherwise, if it is possible to issue a host command, a lock
 *	command is sent out.  If not, the flag Host_lckpending is set
 *	to ensure that the data-page will be locked as soon as possible.
 *	In either event, FALSE is returned, to indicate that the data-page
 *	is not yet locked.
 */
{
    DIS_INTS;				/* disable interrupts */

    Dp_lockcnt++;			/* increment the lock count */
    if (Dp_locked)			/* if data-page already locked */
	{
	Host_unlckpending = FALSE;	/* Clear any pending unlocks */
	ENA_INTS;
	return(TRUE);
	}

    if (NOT Host_lckpending)		/* if not locked and not pending */
	{
	if (NOT h_snd(sizeof(VCMD), V_LOCK))  /* if cannot lock right now, */
	    Host_lckpending = 1;	    /* set flag to lock soon */
	}
					/* LEAVE INTERRUPTS DISABLED */
    return(FALSE);			/* data-page not yet locked */
}


h_unlock ()

/* h_unlock () -- Unlock the host data-page
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Decrement the data-page lock count and set to unlock the
 *	data-page if zero.
 *
 *	*** Note that actual data-page unlocking is not yet implemented ***
 */
{
    DIS_INTS;

    if ((Dp_locked) AND (Dp_lockcnt NE 0))  /* if locked already */
	{
	if (--Dp_lockcnt EQ 0)		/* if time to unlock, */
	    {
	    Host_unlckpending = TRUE;	/* set unlock pending */
	    }
	}

    ENA_INTS;
}

    PKT_STATE
h_alloc (pkt, size)
    PKT_PTR pkt;
    int size;

/* h_alloc (pkt, size) -- Issue allocate command to host
 *	in:	pkt		pointer to i/o packet
 *	in:	size		number of bytes to allocate
 *	return: (PKT_STATE)	HSTAT_WAIT_STATE if cmd sent to IOP
 *						(Stnum incremented)
 *				HCMD_WAIT_STATE if cmd not sent yet
 *						(Stnum left alone)
 *	thrown: (NONE)
 *
 *	Attempts to issue an allocate command to the host IOP to
 *	allocate a host buffer of 'size' bytes.  If the command
 *	is successfully sent, increments Stnum of 'pkt' and returns
 *	HSTAT_WAIT_STATE.  Otherwise, leaves Stnum alone and returns
 *	HCMD_WAIT_STATE so that future processing of the packet will
 *	restart at the current state.
 *
 *	H_alloc() does not check whether the data-page is locked,
 *	so packet processing handlers must ensure this before calling
 *	h_alloc().
 */
{
    DIS_INTS;

    if (h_snd(sizeof(VCMD)+(3*sizeof(PPARAM)), V_PIOCMD, ALLOC_DATA, pkt, size))
	{
	Stnum(*pkt)++;	    /* if command sent, increment Stnum */
	return(HSTAT_WAIT_STATE);	/* and set to wait for status */
	}
    else
	{
	return(HCMD_WAIT_STATE);	/* packet will restart when cmd clear */
	}

    /* NOTE THAT INTERRUPTS ARE LEFT DISABLED FOR PACKET PROCESSING */
}	

    PKT_STATE
h_dealloc (pkt, buf)
    PKT_PTR pkt;
    char *buf;

/* h_dealloc (pkt, buf) -- Issue deallocate command to host
 *	in:	pkt		pointer to i/o packet
 *	in:	buf		address of host buffer to deallocate
 *	return: (PKT_STATE)	HSTAT_WAIT_STATE if cmd sent to IOP
 *						(Stnum incremented)
 *				HCMD_WAIT_STATE if cmd not sent yet
 *						(Stnum left alone)
 *	thrown: (NONE)
 *
 *	Attempts to issue a deallocate command to the host IOP to
 *	deallocate the host buffer at 'buf'.  If the command
 *	is successfully sent, increments Stnum of 'pkt' and returns
 *	HSTAT_WAIT_STATE.  Otherwise, leaves Stnum alone and returns
 *	HCMD_WAIT_STATE so that future processing of the packet will
 *	restart at the current state.
 *
 *	H_dealloc() does not check whether the data-page is locked,
 *	so packet processing handlers must ensure this before calling
 *	h_dealloc().
 *
 *	If pkt==NULL, Stnum is not incremented, naturally.
 */
{
    DIS_INTS;

    if (h_snd(sizeof(VCMD)+(3*sizeof(PPARAM)), V_PIOCMD, DEALL_DATA, pkt, buf))
	{
	if (pkt NE NULL)
		Stnum(*pkt)++;	    /* if command sent, increment Stnum */
	return(HSTAT_WAIT_STATE);	/* and set to wait for status */
	}
    else
	{
	return(HCMD_WAIT_STATE);	/* packet will restart when cmd clear */
	}

    /* NOTE THAT INTERRUPTS ARE LEFT DISABLED FOR PACKET PROCESSING */
}	

    PKT_STATE
h_cmd (pkt, cmd, unit, p1, p2, p3, p4)
    PKT_PTR pkt;
    unsigned cmd;
    unsigned unit;
    UB32 p1, p2, p3, p4;

/* h_cmd (pkt, cmd, unit, p1, p2, p3, p4) -- Issue Physical I/O command to host
 *	in:	pkt		pointer to i/o packet
 *	in:	cmd		command code (including modifier)
 *	in:	unit		IOP unit number
 *	in:	p1,p2,p3,p4	four extra parameters
 *	return: (PKT_STATE)	HSTAT_WAIT_STATE if cmd sent to IOP
 *						(Stnum incremented)
 *				HCMD_WAIT_STATE if cmd not sent yet
 *						(Stnum left alone)
 *	thrown: (NONE)
 *
 *	Attempts to issue a physical I/O command to the host IOP.
 *	If the command is successfully sent, increments Stnum of 'pkt' and
 *	returns HSTAT_WAIT_STATE.  Otherwise, leaves Stnum alone and returns
 *	HCMD_WAIT_STATE so that future processing of the packet will
 *	restart at the current state.
 *
 *	H_cmd() does not check whether the data-page is locked,
 *	so packet processing handlers must ensure this before calling
 *	h_cmd().
 *
 *	If pkt == NULL, Stnum is not touched.
 */
{
    DIS_INTS;

    if (h_snd(sizeof(VCMD)+(6*sizeof(PPARAM)), V_PIOCMD, (cmd | (unit << 16)),
							pkt, p1, p2, p3, p4))
	{
	if (pkt NE NULL)
		Stnum(*pkt)++;	    /* if command sent, increment Stnum */
	return(HSTAT_WAIT_STATE);	/* and set to wait for status */
	}
    else
	{
	return(HCMD_WAIT_STATE);	/* packet will restart when cmd clear */
	}

    /* NOTE THAT INTERRUPTS ARE LEFT DISABLED FOR PACKET PROCESSING */
}	

    BOOLEAN
h_snd (cnt, cmd, p1)
    int cnt;
    VCMD cmd;
    UB32 p1;

/* h_snd (cnt, cmd, p1 [, p2, ...]) -- Send a command to host IOP
 *	in:	cnt		Byte count of cmd in 'blk'
 *	in:	cmd		Host Protocol command code
 *	in:	p1		Parameter list (see notes below)
 *	return: (BOOLEAN)	TRUE if command sent
 *				FALSE if host unavailable (cmd not sent)
 *	thrown: (NONE)
 *
 *	H_snd() is usually invoked with a string of arguments: 'cnt'
 *	is a byte count (sizeof units) of the subsequent argument list.
 *	The second argument should be the Host Protocol Command; this argument
 *	is pushed onto the argument stack as an (int) but ends up being sent
 *	to the host as a (VCMD) (which might only be 16-bits....in which
 *	case the low byte is copied to the high byte to compensate,
 *	and the rest of the argument string, starting at the high byte, is
 *	copied into the Host Protocol Block).
 *
 *	The VCMD is copied last to avoid any possible race conditions.
 *
 *	If the command area in the Host Protocol Block is not available,
 *	FALSE is returned.  Otherwise, it is allocated, the command is
 *	initiated, and TRUE is returned.
 *
 *	This routine must be called with interrupts already disabled!
 */
{
    register UB16 *ptr;

    if (NOT Host_available)
	return (FALSE);			/* Cannot send a new command yet */

    ptr = (UB16*) &cmd;			/* Get address of parameters */

    /* The following lines assume sizeof(VCMD) == 2 rather than 4 */
    *(ptr+1) = *ptr;			/* Copy low byte of cmd to hi */
    ptr++;				/* and point to it */

    Host_available = FALSE;		/* Take control of host cmd area */

    /* Copy 'cnt'-2 bytes from parameter list to host protocol block */
    write_host(&p1, &(Vp1(*Host_protocol)), cnt-2);

    /* Copy 2 bytes from VCMD to host protocol block */
    write_host(ptr, &(Vcmd(*Host_protocol)), 2);

    poke_host();			/* Start a host interrupt */
    return(TRUE);			/* signal command sent */
}


h_ack (code)
    unsigned code;

/* h_ack (code) -- Acknowledge a host command
 *	in:	code		Acknowledge code to send to host
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Copy 'code' into the host command area of the Host Protocol Block.
 *	This overwrites the outstanding host command and signals the host
 *	that the command was received.  Then poke the host interrupt line.
 *	The host should respond by silently (without interrupting back)
 *	clearing the location.
 *
 *	'Code' is generally V_ACK, but could be V_CNCT if acknowledging an
 *	initial host connect.
 */
{
    /* Copy acknowledge code to host protocol block */
    write_host(&code, &(Hcmd(*Host_protocol)), sizeof(VCMD));

    poke_host();			    /* start a host interrupt */
}


host_init ()

/* host_init () -- Initialize VIOS<->Host flags
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Initialize host flags to indicate no host connection
 *	as of yet.  Note that the use of this routine may change
 *	when PROM versions are developed.
 */
{
    Host_available = Host_connected = Host_crashed =
    Host_cmdpending = Host_lckpending = Host_unlckpending =
	    Dp_locked = FALSE;		    /* clear flags */
    Dp_lockcnt = 0;			    /* Zero d-p lock count */
}

#ifdef REV_2A /****************** CPU REVISION-2A **********************/

host_businit ()

/* host_bus_init () -- Handle Host Bus Init interrupts
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	This routine is the interrupt handler for the HOST_INIT interrupt.
 *	If the host was already connected, call host_crash().
 */
{
    if (Host_connected)		/* dismiss interrupt if host not connected */
	host_crash();		/* otherwise, stop trying to write to it */
}
#endif /************************* CPU REVISION-2A **********************/


host_crash ()

/* host_crash () -- Handle a host crash or IOP disconnect
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	This code is called if the host disconnects or if the
 *	VIOS thinks the host is connected and yet the host issues
 *	a connect command (indicating that it had crashed and been
 *	rebooted).
 *
 *	Right now, this code crashes the VIOS.  Later, it might
 *	do something more reasonable, like prepare the VIOS for resetting
 *	the IOP when it comes back up.
 */
{
    errpri("\nHost Is Crashed!!\n", NULL);

    Host_crashed = TRUE;		/* Mark host down */
    brkvios();
}


host_reboot (csr)
    unsigned csr;

/* host_reboot (csr) -- Handle a host IOP connection
 *	in:	csr		CSR value (address of Host Protocol Block)
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	This code is called when the host IOP is trying to connect
 *	to the VIOS.  Specifically, the VIOS has been interrupted with a
 *	value in the CSR greater than MAXHOSTFUNC and, therefore, the
 *	address, in host memory ( <64KB ), of the Host Protocol Block.
 *
 *	Host_reboot() does the following:
 *		1) Reset all flags associated with host status.
 *		2) Copy 'csr' into the variable Host_protocol.
 *		3) Acknowledge the host command with V_CNCT.
 *		4) Re-activate all i/o packets waiting for a host
 *		   connection.
 */
{
    Host_available = Host_connected = TRUE; /* mark IOP connected */
    Host_cmdpending = Host_unlckpending = Dp_locked = FALSE;    /* clear flgs */

    /* Read the interrupt vector from the Host Protocol Block */
    read_host(Host_protocol = (PROTOCOL_BLOCK*)csr, &Host_vector, sizeof(VCMD));

    h_ack(V_CNCT);			    /* Acknowledge connection */

    if (Host_crashed)			/* If the host had crashed, */
	{
	Host_crashed = FALSE;		/* reset the crash flag */

	/* Insert host restart code, if any */

	}

    if (Host_lckpending)		/* if pending data-page lock */
	{
	Host_lckpending = -1;		/* pending, but issued */
	h_snd(sizeof(VCMD), V_LOCK);	/* issue lock request */
	}
    else
	wq_activate(HCMD_WAIT_STATE, 1);	/* pkt waiting to issue cmd? */

    wq_activate(HCNCT_WAIT_STATE, 0);	    /* start pkts waiting for host */
}


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 **********************/

}


write_dp (from, offset, bcnt)
    char *from;
    unsigned offset;
    int bcnt;

/* write_dp (from, offset, bcnt) -- Copy from local to host data-page
 *	in:	from		Address of source
 *	in:	offset		Destination data-page offset
 *	in:	bcnt		Number of bytes to copy
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Copy 'bcnt' bytes from local memory at 'from' to 'offset' in host
 *	data-page.  'Offset' is added to the address of the data-page
 *	(Dp_address) and write_host() is called to perform the actual transfer.
 *
 *	The data-page had better be locked already.
 *
 *	If the host is not connected, this routine becomes a no-op, with no
 *	error indication returned.
 */
{
    write_host (from, offset+Dp_address, bcnt); /* Copy buffer */
}


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 **********************/

}


read_dp (offset, to, bcnt)
    unsigned offset;
    char *to;
    int bcnt;

/* read_dp (offset, to, bcnt) -- Copy from host data-page to local memory
 *	in:	offset		Source data-page offset
 *	in:	to		Address of destination
 *	in:	bcnt		Number of bytes to copy
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Copy 'bcnt' bytes from 'offset' in host data-page to local memory
 *	at 'to'.  'Offset' is added to the address of the data-page
 *	(Dp_address) and read_host() is called to perform the actual transfer.
 *
 *	The data-page had better be locked already.
 *
 *	If the host is not connected, this routine becomes a no-op, with no
 *	error indication returned.
 */
{
    read_host (offset+Dp_address, to, bcnt);	/* Copy buffer */
}


poke_host ()

/* poke_host () -- Interrupt the host
 *	return: (NONE)
 *	thrown: (NONE)
 *
 *	Initiate a host interrrupt by setting the host interrupt vector
 *	(Host_vector) into the Host Interrupt Register (Host_INT).
 *
 *	If the host is not connected, this routine becomes a no-op, with no
 *	error indication returned.  If the previous interrupt was not yet
 *	acknowledged by the host, this routine becomes a no-op, since the
 *	currently pending interrupt will suffice.
 */
{
    if ( (Host_connected) AND
     (((*((char*)(INT_IPND0 + (4 * (HOST_IPND/8))))) & (1 << HOST_IPND)) EQ 0) )
	*Host_INT = (UB16) Host_vector;
}
