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

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


    PKT_STATE			/* Throws: V_NO_DEVICE / V_ILL_FUNCTION */
vctrl_handler (pkt)
    register PKT_PTR pkt;

/* vctrl_handler (pkt) -- Do initial processing of VIOS Control Requests
 *
 *	in:	pkt		I/O Packet to process
 *	return:	(PKT_STATE)	next I/O packet state
 *	thrown:	V_NO_DEVICE	device spec supplied on CREATEVD function
 *				or missing if required
 *		V_ILL_FUNCTION	illegal virtual device control function
 *
 *	Dispatch i/o packet to appropriate Virtual Device Control handler.
 *	Make sure that there is (or isn't) a target device if required (or not).
 */
{
    register IO_PACKET *pk;	/* dereferenced ptr */
    VDD_PTR dev;

    pk = *pkt;

    switch (Stnum(pk))
	{
    case 0:
	switch (Fcode(pk))
	    {
	case CREATEVD:
	    if (VDevice(pk) NE NULL)  throw(V_NO_DEVICE);
	    Handler(pk) = cre_handler;		/* Create a VDD */
	    return(VIO_RUN_STATE);		/* continue with new handler */

	case REMOVEVD:
	    Handler(pk) = rem_handler;		/* Remove a VDD */
	    break;

	case ASSIGNVD:
	    Handler(pk) = ass_handler;		/* Assign VDD units */
	    break;

	default:
	    throw(V_ILL_FUNCTION);

	    } /* switch function-code */

	if (VDevice(pk) EQ NULL)  throw(V_NO_DEVICE);	/* must be a VDD */

	return(VIO_RUN_STATE);		/* continue with new handler */

    default:

#ifdef DEBUG3   /*************************************************************/
	error ("Bad stnum in pkt @%x (vctrl_handler)", *pkt);
#endif /* DEBUG3 *************************************************************/

	} /* switch Stnum */
}

    PKT_STATE					/* Throws: various errors */
cre_handler (pkt)
    PKT_PTR pkt;

/* cre_handler (pkt) -- Process Create Virtual Device Requests
 *
 *	in:	pkt		I/O Packet to process
 *	return:	(PKT_STATE)	next I/O packet state
 *	thrown:	V_BAD_PARAMS	Bad parameter in request
 *		V_ILL_MODIFIER	Bad modifier in request function
 *		V_DUP_NAME	Virtual Device name already in use
 *		V_SUCCESS	Packet successfully processed
 *
 *	Process VIOS requests to create a Virtual Device.
 *	Process as follows:
 *		1) If invalid device name or class specification,
 *			throw V_BAD_PARAMS.
 *		2) If invalid function modifier,
 *			throw V_ILL_MODIFIER.
 *		3) Allocate a VDD (if allocation failure, requeue).
 *		4) If there is a device with the same name and O.S. owner,
 *			throw V_DUP_NAME.
 *		5) Look up appropriate device class handling module and set
 *		   its bufptr in the VDD.  If class not valid
 *			throw V_BAD_PARAMS.
 *		6) If REM_ON_DEA, set VDD to be removed on last detach.
 *		7) Requeue name string to VDD and clear in packet, so that
 *		   io_complete() doesn't de-allocate it.
 *		8) Add the new VDD onto the Virtual Device List and set
 *		   its number into the packet return status.
 *		9) Call the class-dependent VDCREATE handler; handler may
 *			throw ???.
 *		10)Throw V_SUCCESS.
 */
{
    register IO_PACKET *pk;	/* dereferenced i/o pointer */
    register char **name;	/* bufptr to device name */
    register VDD *vd;		/* ptr to VDD */
    VDD_PTR vdd;	/* bufptr to VDD  ** Must not be a register!! ** */
    OS_IDENT rid;		/* request os-id */
    unsigned mod;		/* request modifier */

    pk = *pkt;		/* pk must be refreshed after alloc() */
    name = Auxparam(pk);	/* get bufptr to device name */
    if ( (name EQ NULL) OR (**name EQ '\0') OR
    		(strlen(*name) GT MAX_NAMELENGTH) OR
			((Fcode(pk) EQ CREATEVD) AND (Param1(pk) EQ 0)) )
	throw(V_BAD_PARAMS);	/* not a bufptr; no name or class */

    rid = Osid(pk);		/* OS of owner of device */
    mod = Fmod(pk);		/* function modifier (REM_ON_DEA) */


				/* CREATEVD code follows: */
    switch (Stnum(pk))
	{
    case 0:
	if ( (mod NE 0) AND (mod NE REM_ON_DEA) )
	    throw(V_ILL_MODIFIER);

	Stnum(pk)++;

    case 1:
	switch (catch())
	    {
	case 0:
	    vdd = vddalloc(rid);	/* allocate a VDD */
	    pk = *pkt;		/* in case of pool compaction */
	    if (vname_search(*name, rid) NE NULL)
		{
		throw(V_DUP_NAME);	/* duplicate VD name */
		}
	    vd = *vdd;
	    Devname(vd) = name;			/* bufptr to name */

	    /* Set device class and look up class handling module */
	    /* Cls_search() throws V_BAD_PARAMS if no class handler */

	    Classmod(vd) = cls_search(Devclass(vd) = Param1(pk));

	    if (mod & REM_ON_DEA)		/* remove device when */
		Detachremove(vd) = TRUE;	/* no longer needed */

	/* Call class-dependent handler with create virtual device command */
	    (*Classdispatcher(vd)) (VDCREATE, pkt, vdd);	/* throws ??? */

	    Auxparam(pk) = NULL;		/* prevent de-alloc */
	    q_item(vdd, &Vdevices);		/* add to VDD list */
	    Stataux2(pk) = (int) vdd;		/* return VD number */

	    uncatch();				/* end of protected code */
	    throw(V_SUCCESS);			/* successful completion */

	case V_ALL_FAILURE:
	    return(ALL_WAIT_STATE);	/* continue when pool is free */

	default:
	    vfree(vdd);		/* error detected...free vdd */
	    rethrow();			/* pass error along */
	    } /*switch (catch)*/

	break; /*case 1*/

    default:

#ifdef DEBUG3   /*************************************************************/
	error ("Bad stnum in packet @%x (create_vd)", *pkt);
#endif /* DEBUG3 *************************************************************/

	} /*switch Stnum*/

}

    PKT_STATE					/* Throws: various errors */
rem_handler (pkt)
    register PKT_PTR pkt;

/* rem_handler (pkt) -- Process Remove Virtual Device Requests
 *
 *	in:	pkt		I/O Packet to process
 *	return:	(PKT_STATE)	next I/O packet state
 *	thrown:	V_SUCCESS	Packet successfully processed
 *
 *	Process VIOS requests to remove a Virtual Device.
 *	Process as follows:
 *		1) Remove all references to the target device.
 *		   See rem_references() (in ctrlsubs.c).
 *		2) Set device to be removed when i/o completes.
 *		   This means it will be removed as soon as this packet
 *		   completes, if it is the only outstanding i/o.
 *		   Otherwise, removal will wait for the rest.
 */
{
    register VDD_PTR vdd;	/* VDD to remove */

    vdd = VDevice(*pkt);	/* VDD has already been verified */
    rem_references(vdd);	/* remove all references to this device */

#ifdef DEBUG2   /*************************************************************/
    if (Attachments(*vdd) NE 0)
	error("Rem_handler couldn't run down vdd at %x", *vdd);
#endif /* DEBUG2 *************************************************************/

    Rundown(*vdd) = Compremove(*vdd) = TRUE;	/* remove when i/o done */
    throw(V_SUCCESS);		/* successful completion */
}

    PKT_STATE					/* Throws: various errors */
ass_handler (pkt)
    register PKT_PTR pkt;

/* ass_handler (pkt) -- Process Assign Virtual Device Requests
 *
 *	in:	pkt		I/O Packet to process
 *	return:	(PKT_STATE)	next I/O packet state
 *	thrown:	V_DEV_BUSY	Target device has active i/o
 *		V_ILL_MODIFIER	Push/Pop Primary Input not only subcommand
 *				or assign secondary unit to Physical Device
 *		V_BAD_PARAMS	Invalid secondary device specification
 *		V_CLASS_MISMATCH Secondary device class not same as target dev
 *				or Push/Pop to target that isn't Rec-Seq
 *		V_CIRCULAR	Assignment would cause circular redirection
 *		V_SUCCESS	Packet successfully processed
 *
 *	Process VIOS requests to make a Virtual Device unit attachment to
 *	a virtual/physical device.
 *	Process as follows:
 *		1) If target virtual device is busy (i/o count <> 0),
 *			throw V_DEV_BUSY.
 *		2) If a redirect device bufptr is not supplied, verify that
 *		   either a device name is missing, or that it refers to a
 *		   valid device.  If not, or if both bufptr and name supplied,
 *			throw V_BAD_PARAMS.
 *		3) If redirect bufptr is supplied, verify that it is a valid
 *		   VDD or PDD bufptr.  Else,
 *			throw V_NO_DEVICE.
 * 		4) If Push / Pop Primary Input, if other subcommands,
 *			throw V_ILL_MODIFIER.
 *		5) If Push Primary Input, if secondary device not supplied,
 *			throw V_BAD_PARAMS.
 *		6) If POP Primary Input, if secondary device supplied,
 *			throw V_BAD_PARAMS.
 *		7) If Push / Pop Primary Input, if not Record Sequential class,
 *			throw V_CLASS_MISMATCH.
 *		8) If Pop Primary Input, do it (see pop_unit() in ctrlsubs.c) &
 *			throw V_SUCCESS.
 *		9) If secondary device is a Physical Device and secondary units
 *		   are being assigned (e.g. Journal Output),
 *			throw V_ILL_MODIFIER.
 *		10)Trace assignments down input and/or output chains (see
 *		   trc_input() / trc_output() in ctrlsubs.c).  If error,
 *			throw V_CIRCULAR.
 *		11)If secondary device not same class as target device,
 *			throw V_CLASS_MISMATCH.
 *		12)If Push Primary Input, allocate an extra VDD.
 *		   If allocation failure, requeue packet.
 *		   If O.S. VDD limit reached, vddalloc() will
 *	TO DO:		throw ???
 *
 *		13)Handle all the specified unit assignments and
 *			throw V_SUCCESS.
 */
{
#define ALL_INP  (PRI_INP|ERR_INP)
#define ALL_OUT  (PRI_OUT|JOU_INP|JOU_OUT)
#define SEC_IO   (ERR_INP|JOU_INP|JOU_OUT)

    register IO_PACKET *pk;	/* dereferenced i/o packet */
    register VDD_PTR vdd;	/* Virtual Device to receive assignments */
    char **devname;		/* V/P device name to assign */
    VDD_PTR dev;		/* V/P device to assign */
    VDD_PTR pushdev;		/* ptr to new vdd, if PUSH_PRI_INP */
    register unsigned mod;	/* function modifier byte */

    pk = *pkt;			/* must be refreshed after alloc() */
    mod = Fmod(pk);			/* set modifier */

    vdd = VDevice(pk);			/* set request device */
    if (Outstanding_IO(*vdd) GT 1)  throw(V_DEV_BUSY);

    /* Either a device number (bufptr) was specified in Param1, or an ASCIZ */
    /* device name (bufptr) of the device was supplied in Auxparam. */
    /* In either event, set 'dev' to the verified device bufptr */

    devname = Auxparam(pk);		/* Get redirect device name, if any */

    if ((dev = (VDD_PTR) Param1(pk)) EQ NULL)	/* Get redirect bufptr */
	{
	if (devname NE NULL)		/* No VDD bufptr, but name specified */
	    {
	    if ( (devname EQ NULL) OR (**devname EQ '\0') )
		throw(V_BAD_PARAMS);	/* Bad name specification */
	    
	    if (catch() EQ 0)
		{
		dev = vdname_search(*devname, Osid(pk));
		uncatch();		/* Name of vd was specified */
		}
	    else
		{
		if ((dev = (VDD_PTR) pname_search(*devname)) EQ NULL)
		    throw(V_BAD_PARAMS);	/* Named device not found */
		}
	    } /*if-device name*/
	} /*if-no device number*/
    
    else				/* Redirect device bufptr specified */
	{
	if (devname NE NULL)		/* If name and number supplied, */
	    throw(V_BAD_PARAMS);	/* device was overspecified */
	
	if (catch() EQ 0)
	    {
	    checkvd(dev, Osid(pk));	/* Device bufptr is a VDD */
	    uncatch();
	    }
	else
	    {
	    checkpd(dev);		/* throw V_NO_DEVICE if not a PDD */
	    }
	} /*if-bufptr supplied*/
    

    /* At this point, 'dev' is a valid device bufptr, or NULL */

    switch (Stnum(pk))
	{
    case 0:
	/* First, handle Pop Primary Input requests */
	if (mod & POP_PRI_INP)
	    {
	    if (mod & (~POP_PRI_INP))  throw(V_ILL_MODIFIER);
	    if (dev NE NULL)  throw(V_BAD_PARAMS);
	    if (Devclass(*vdd) NE REC_SEQ)  throw(V_CLASS_MISMATCH);
	    pop_unit(vdd);
	    throw(V_SUCCESS);	/* finished processing */
	    }

	/* If not POP....check that modifier is valid */
	if (mod & PUSH_PRI_INP)
	    {
	    if (mod & (~PUSH_PRI_INP))  throw(V_ILL_MODIFIER);
	    if (dev EQ NULL)  throw(V_BAD_PARAMS);
	    if (Devclass(*vdd) NE REC_SEQ)  throw(V_CLASS_MISMATCH);
	    }
	else
	    {
	    if (mod & (~(ALL_INP|ALL_OUT)))  throw(V_ILL_MODIFIER);
	    }

	Stnum(pk)++;			/* set state number to 1 */

    case 1:
	/* Now check that device redirection is legal */
	if (dev NE NULL)
	    {
	    if (Devtype(*dev) EQ VDTYPE) 	/* VDD - Ensure not circular */
		{
		if (mod & ALL_OUT) trc_output(dev,vdd);
		if (mod & (ALL_INP|PUSH_PRI_INP)) trc_input(dev,vdd);
		}
	    else
		{				/* PDD - Ensure primary only */
		if (mod & SEC_IO)  throw(V_ILL_MODIFIER);
		}

	if (Devclass(*dev) NE Devclass(*vdd))  throw(V_CLASS_MISMATCH);

	    } /*if-dev not NULL*/

	/* Request is ok */

	/* Perform PUSH operation */
	if (mod & PUSH_PRI_INP)
	    {
	    			/* if no primary to push, just set it later */
	    if (Primaryinput(*vdd) EQ NULL)  mod = PRI_INP;
	    else
		{
		pushdev = vddalloc(HYPER_ID);	/* allocate */
		    			/* push dev to primary input of vdd */
		Devclass(*pushdev) = REC_SEQ;
		push_unit(vdd,dev,pushdev);
		throw (V_SUCCESS);		/* push is only function */
		} /*else*/
	    } /*if PUSH*/

	/* Handle all other unit assignments */
	if (mod & PRI_INP)  reassign (&Primaryinput(*vdd), dev);
	if (mod & PRI_OUT)  reassign (&Primaryoutput(*vdd), dev);
	if (mod & ERR_INP)  reassign (&Errorinput(*vdd), dev);
	if (mod & JOU_INP)  reassign (&Journalinput(*vdd), dev);
	if (mod & JOU_OUT)  reassign (&Journaloutput(*vdd), dev);

	throw (V_SUCCESS);
	}
}

