
/*
 * pktsubs.c: version 3.2 of 6/4/85
 *
 *  pktsubs.c -- I/O Packet management subroutines
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg	November, 1984
 */
#ifdef SCCS
static char *sccsid = "@(#)pktsubs.c	3.2";
#endif

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


    PKT_PTR					/* Throws:  V_ALL_FAILURE */
allpkt ()

/* allpkt () -- Allocate and initialize an I/O packet
 *
 *	return:	(PKT_PTR)	bufptr to allocated i/o packet
 *	thrown:	V_ALL_FAILURE	pool allocation failure
 *
 *	Allocate an i/o packet from system pool, clear it, and set the
 *	handler to init_request().
 *
 *	Initialization assumes that NULL == NEW_STATE == 0.
 *
 *	Throws V_ALL_FAILURE if pool allocation fails.
 */
{
    register PKT_PTR pkt;
    register int i;
    register char *ptr;

    ptr = (char*) *(pkt=(PKT_PTR) valloc_pkt());
    for (i=0; i<sizeof(IO_PACKET); i++)
	*ptr++ = '\0';		/* Init packet to all zeros */
				/* This assumes that NEW_STATE = NULL == 0 */

    Handler(*pkt) = init_request;	/* Assume this will be Virtual I/O */
    return(pkt);
}

    PKT_PTR					/* Throws:  V_ALL_FAILURE */
duppkt (ppkt)
    PKT_PTR ppkt;

/* duppkt (ppkt) -- Duplicate an I/O Packet as an offspring of current packet
 *
 *	in:	ppkt		parent i/o packet bufptr
 *	return:	(PKT_PTR)	bufptr to allocated i/o packet
 *	thrown:	V_ALL_FAILURE	pool allocation failure
 *
 *	Calls allpkt() to allocate and clear an i/o packet from system pool.
 *	Duppkt() then copies the entire parent packet into the new packet.
 *	The parent i/o count is incremented and the new packet parent ptr
 *	is set to 'ppkt'.  Also, the new packet is set to throw away its
 *	status, although it is easily modified to copy its status to the
 *	parent status (by setting Parentstat(pkt)).
 *	Finally, the i/o count, i/o status, and device spec are cleared...
 *	these must also be set explicitly by the calling routines.
 *
 *	See io_complete() (in viomain.c) to see how completion status is
 *	passed around.
 *
 *	Initialization assumes that NULL == NEW_STATE == 0.
 *
 *	Throws V_ALL_FAILURE if pool allocation fails.
 */
{
    PKT_PTR opkt;		/* offspring packet bufptr */
    register IO_PACKET *op;	/* offspring packet */
    IO_PACKET *pp;		/* parent packet */
    register char *oc;		/* offspring byte ptr */
    register char *pc;		/* parent byte ptr */
    int i;

    oc = (char*) (op = *(opkt = allpkt()));	/* get new pkt and set ptrs */
    pc = (char*) *ppkt;				/* set ptr to parent */
    for (i=0; i < sizeof(IO_PACKET); i++)
	*oc++ = *pc++;		/* copy parent packet */

    pp = *(Parent(op) = ppkt);	/* set parent ptr */
    Iocount(pp)++;		/* increment parent I/O count */

    Compos(op) = VIOS_ID;	/* set completion ID */
    Comproutine(op) = NULL;	/* no comp routine unless set explicitly */
    Parentstat(op) = NULL;	/* no status transfer unless set explicitly */
    Iocount(op) = 0;		/* no i/o count yet */
    VDevice(op) = NULL;		/* device must be reset explicitly */

    Statcode(op) = V_PENDING;	/* Reset all i/o status */
    Statauxcode(op) = 0;
    Stataux1(op) = 0;		/* Zero status, in case of journaling */
    Stataux2(op) = 0;
    Stataux3(op) = 0;
    Stataux4(op) = 0;

    return(opkt);		/* return bufptr to offspring */
}

    PDD_PTR					    /* throw: V_ALL_FAILURE */
o_dup (pkt)
    PKT_PTR pkt;

/* o_dup (pkt) -- Spawn a journal output copy of the current packet
 *	in:	pkt		Current i/o pkt
 *	return:	(PDD_PTR)	primary output unit of packet device
 *	thrown:	V_ALL_FAILURE	allocation failure on child packet
 *
 *	If the Journal-output unit of the 'pkt' device is non_NULL,
 *	duplicate the packet and queue the child to it.
 *	Status from the child packet will be thrown away.
 *
 *	If a child pkt may not be allocated, throw V_ALL_FAILURE.
 */
{
    register VDD_PTR dev;

    _dup (pkt, Journaloutput(*(dev = VDevice(*pkt))));
    return((PDD_PTR) Primaryoutput(*dev));	/* return primary output unit */
}

    PDD_PTR					    /* throw: V_ALL_FAILURE */
i_dup (pkt)
    PKT_PTR pkt;

/* i_dup (pkt) -- Spawn an error input copy of the current packet
 *	in:	pkt		Current i/o pkt
 *	return:	(PDD_PTR)	primary input unit of packet device
 *	thrown:	V_ALL_FAILURE	allocation failure on child packet
 *
 *	If the Error-input unit of the 'pkt' device is non_NULL,
 *	duplicate the packet and queue the child to it.
 *	Status from the child packet will be thrown away.
 *
 *	If a child pkt may not be allocated, throw V_ALL_FAILURE.
 */
{
    register VDD_PTR dev;

    _dup (pkt, Errorinput(*(dev = VDevice(*pkt))));
    return((PDD_PTR) Primaryinput(*dev));	/* return primary output unit */
}

    PDD_PTR					    /* throw: V_ALL_FAILURE */
io_dup (pkt)
    PKT_PTR pkt;

/* io_dup (pkt) -- Spawn copies of the current packet
 *	in:	pkt		Current i/o pkt
 *	return:	(PDD_PTR)	primary input unit of packet device
 *				(or primary-output, if the other is NULL)
 *	thrown:	V_ALL_FAILURE	allocation failure on child packet
 *
 *	If there are Journal-output and/or Error_input devices for the
 *	device specified in 'pkt', copies of the current packet will be
 *	spawned to them.  If the Primary-output and Primary-Input units
 *	do not match, a copy of the packet will be spawned to the Primary-
 *	Output unit.
 *
 *	A pointer to the Primary-Input device is returned, unless it is NULL,
 *	in which case the Primary-Output unit, if any, is returned.
 *
 *	Status from all child packets will be thrown away.
 *
 *	If a child pkt may not be allocated, throw V_ALL_FAILURE.
 *	No packets will be allocated if V_ALL_FAILURE is thrown, so that
 *	the current packet may be restarted correctly.
 */
{
    register VDD *dv;
    register int cnt;
    register PDD_PTR rdev;
    register VDD_PTR podev;
    register VDD_PTR pidev;
    VDD_PTR jodev;
    VDD_PTR eidev;

    if ( (jodev = Journaloutput(dv = *VDevice(*pkt))) NE NULL )
	cnt = sizeof(IO_PACKET);	/* cnt number of pkts to spawn */
    else
	cnt = 0;			/* init counter */

    if ( (eidev = Errorinput(dv)) NE NULL )
	cnt += sizeof(IO_PACKET);	/* cnt another pkt to spawn */

    podev = Primaryoutput(dv);

    if ( (rdev = (PDD_PTR) (pidev = Primaryinput(dv))) EQ NULL )
	rdev = (PDD_PTR) podev;		/* if only one primary unit return it */
    else
	if (podev NE NULL)		/* if both primary units not NULL */
	    {
	    if (podev NE pidev)		/* spawn output if not same as input */
		cnt += sizeof(IO_PACKET);
	    else
		podev = NULL;		/* but not if they are the same */
	    }

    if (cnt NE 0)			/* if any packets are to be alloc'ed */
	free(valloc(cnt));		/* allocate and release space for all */
					/* V_ALL_FAILURE thrown if not enough */

    _dup(pkt, jodev);		/* copy to journal output, if any */
    _dup(pkt, eidev);		/* copy to error input, if any */
    _dup(pkt, podev);		/* copy to primary output, if not pri-input */

    return(rdev);		/* return primary input unit */
}

						    /* throw: V_ALL_FAILURE */
_dup (pkt, dev)
    PKT_PTR pkt;
    VDD_PTR dev;

/* _dup (pkt, dev) -- Duplicate the current packet and reset its device
 *	in:	pkt		Current i/o pkt
 *	in:	dev		New device for spawned pkt
 *	return:	(NONE)
 *	thrown:	V_ALL_FAILURE	allocation failure on child packet
 *
 *	Duplicate the packet, reset the device, and queue the child to it.
 *	Status from the child packet will be thrown away.
 *
 *	If a child pkt may not be allocated, throw V_ALL_FAILURE.
 */
{
    register PKT_PTR npkt;

    if (dev NE NULL)		/* if device present */
	{
	npkt = duppkt(pkt);		/* copy packet (Parenstat cleared) */
	resetdev (npkt, dev);		/* reset device */
	q_virtual(npkt);		/* return to current state/number */
	}
}

    PDD_PTR
fnd_inp_pdd (dev)
    register VDD_PTR dev;

/* fnd_inp_pdd (dev) -- Find a physical device at the end of Primary Inputs
 *	in:	dev		Physical Device Descriptor (or NULL)
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Trace down all the Primary Input units until a Physical Device
 *	is found.  Return its PDD.  If no Physical Device at end, return NULL.
 */
{
    while (dev NE NULL)		/* while any more devices in chain */
	{
	if (Devtype(*dev) EQ PDTYPE)	/* if this is the PDD at last */
	    break;			/* then return it */

	dev = Primaryinput(*dev);	/* otherwise, go down chain */
	}
    return((PDD_PTR)dev);		/* return PDD or NULL */
}

    PDD_PTR
fnd_out_pdd (dev)
    register VDD_PTR dev;

/* fnd_out_pdd (dev) -- Find a physical device at the end of Primary Outputs
 *	in:	dev		Physical Device Descriptor (or NULL)
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Trace down all the Primary Output units until a Physical Device
 *	is found.  Return its PDD.  If no Physical Device at end, return NULL.
 */
{
    while (dev NE NULL)		/* while any more devices in chain */
	{
	if (Devtype(*dev) EQ PDTYPE)	/* if this is the PDD at last */
	    break;			/* then return it */

	dev = Primaryoutput(*dev);	/* otherwise, go down chain */
	}
    return((PDD_PTR)dev);		/* return PDD or NULL */
}
