/*  recseq.c -- Class-dependent routines for Record-Sequential Devices
 *  copyright (c) American Information Systems Corporation
 *	Daniel Steinberg
 *	November, 1984
 *
 */

#include "viosconf.h"
#include "iopacket.h"
#include "pktfuncs.h"
#include "viocmds.h"
#include "viostatus.h"
#include "piostatus.h"
#include "handlers.h"

    PKT_STATE					/* throws: various errors */
recs_dispatcher (cmd, pkt, dev)
    int cmd;
    PKT_PTR pkt;
    VDD_PTR dev;

/* recs_dispatcher (cmd, pkt, dev) -- Class-dependent i/o mapping dispatcher
 *
 *	in:	cmd		Type of VIOS request to handle
 *	in:	pkt		Current i/o packet ptr, if any
 *	in:	dev		[if VDCREATE or VDREMOVE]
 *	return:	(PKT_STATE)	Current state of i/o packet
 *	thrown:	V_ILL_MODIFIER	bad i/o request modifier
 *
 *	Perform i/o request handling for Record-Sequential Devices.
 *
 *	This routine is called during the processing of an i/o packet to perform
 *	any class-dependent operations or mapping of requests.  It must either
 *	verify that the request is valid for the given device class and initiate
 *	any class-dependent actions, or dispatch to a routine that will do so.
 *	(See DOCS/classdispatcher for information on calling conditions.)
 *
 *	Takes the following action:
 *		cmd == VDCREATE
 *		cmd == VDREMOVE
 *		cmd == PDCREATE
 *		cmd == PDREMOVE
 *		cmd == VDWRITE	If the primary output unit is NULL,
 *					throws V_SUCCESS. (write to Black Hole)
 *				Resets the packet device to the primary output
 *				unit.  If this is another Virtual Device,
 *					returns VIO_RUN_STATE.
 *				If Physical Device, calls q2device() (in
 *				rdwrtsubs.c) and returns its value.
 *		cmd == VDREAD	If the primary input unit is NULL, attempt to
 *				pop an eot-input unit.  If the eot-input queue
 *				is (or becomes) empty and no input device is
 *				found,
 *					throws V_NO_DEVICE.
 *				Resets the packet device to the primary input
 *				unit.  If this is another Virtual Device,
 *					returns VIO_RUN_STATE.
 *				If Physical Device, clears the RECORDMODE
 *				modifiers (unless R_STREAM device) and calls
 *				q2device() (in rdwrtsubs.c) returning its value.
 *		cmd == VDIOCTRL
 *		cmd == PDIOCTRL
 *		cmd == VDMAPPOOL
 *		cmd == VDINFO	Copy four parameters into user buffer of 'pkt':
 *					1) Recs_rlength
 *					2) R_STREAM / R_BLOCK
 *					3) Recs_bytenum
 *					4) 0
 *
 *	Throws V_ILL_MODIFIER if the i/o request modifier is not valid for this
 *	class of devices.
 */
{
    register VDD *dv;
    register IO_PACKET *pk;
    register PDD_PTR pdev;
    register UB32 flg;

    pk = *pkt;			/* dereference ptrs */
    dv = *dev;

    switch (cmd)
	{
    case VDCREATE:
    case VDREMOVE:
	break;

    case VDREAD:
	while ((pdev = (PDD_PTR) Primaryinput(dv)) EQ NULL)
	    {
	    if (Recs_eot(*pdev) EQ NULL)
		throw(V_NO_DEVICE);		/* nothing left to pop */

	    pop_unit(dev);			/* get a new input unit */
	    } /*while*/
	
	resetdev(pkt,pdev);		/* point pkt to new device */
	if (Devtype(*pdev) EQ VDTYPE)
	    break;			/* requeue pkt with new Virtual Dev */

			    /* continue if requeueing to Physical device */
	switch (Fmod(pk))
	    {
	case RAWMODE:
	case RECORDMODE:
	case ECHO_RECORDMODE:
		    /* if blocked-record device ignore stream modes */
	if (Recs_imode(*pdev) EQ R_BLOCK)
		Fmod(pk) = RECORDMODE;

	case BLOCKMODE:
	    break;

	default:
	    throw(V_ILL_MODIFIER);
	    } /*switch-Fmod*/

	return(q2device(pkt));	/* queue to physical device handler */

    case VDWRITE:
	if ((pdev = (PDD_PTR) Primaryoutput(dv)) EQ NULL)	/* get unit */
	    throw(V_SUCCESS);		/* write to Black Hole */
	
	resetdev(pkt,pdev);		/* set pkt to point to new device */
	if (Devtype(*pdev) EQ VDTYPE)
	    break;			/* requeue to virtual dev handler */

	return(q2device(pkt));		/* queue to device handler */

    case VDIOCTRL:
	if (Devtype(dv) EQ VDTYPE)	/* if I/O Control to Virtual Device */
	    {
	    /*
	     * The following cases set call one of:
	     *   i_dup  -  spawn pkt to err-input device and return pri-inp unit
	     *   o_dup  -  spawn pkt to jou-output and return pri-output unit
	     *  io_dup  -  spawn pkt to all of above
	     */

	    switch(Fmod(pk))		/* determine what to spawn */
		{
	    case REWIND:
	    case UNLOAD_MEDIA:
		pdev = io_dup(pkt);	/* spawn both input & output */
		break;

	    case WRITE_EOF:
	    case WRITE_EOT:
		pdev = o_dup(pkt);		/* spawn output */
		break;

	    case SKIP_RECORDS:
	    case SKIP_FILES:
		pdev = i_dup(pkt);		/* spawn input */
		break;

#ifdef DEBUG3   /*************************************************************/
	    default:
		throw(V_ILL_FUNCTION);	/* unknown virtual dev ctrl function */
#endif /* DEBUG3 *************************************************************/
		} /*switch-Fmod*/

	    if (pdev EQ NULL)
		throw(V_SUCCESS);		/* pkt will wait for any kids */

	    resetdev(pkt,(dev = (VDD_PTR) pdev));	/* point to nxt dev */
	    if (Devtype(dv = *dev) EQ VDTYPE)
		break;			/* (to end) requeue if still a VD */

	    pk = *pkt;			/* refresh pointer */

	    } /*if-VD*/

	    /* drop thru if pkt now points to a PDD */

    case PDIOCTRL:
	switch (Fmod(pk))		/* check device function */
	    {
	case REWIND:
	    if (!(Recs_rwdok(dv)))	/* if device cannot rewind */
		throw(V_SUCCESS);	/* packet is complete */
	    break;
	
	case UNLOAD_MEDIA:
	case WRITE_EOF:
	case WRITE_EOT:
	case SKIP_RECORDS:
	case SKIP_FILES:

	/* DO PARAMETER CHECKING SOON!!! */

	case INP_BAUD:
	    Rp_ibaud((PDD*)dv) = Param1(pk);	/* set input baud rate */
	    break;

	case OUT_BAUD:
	    Rp_obaud((PDD*)dv) = Param1(pk);	/* set output baud rate */
	    break;

	case INP_THROTTLE:
	    Rp_ithrottle((PDD*)dv) = Param1(pk);	/* input throttle */
	    break;

	case OUT_THROTTLE:
	    Rp_othrottle((PDD*)dv) = Param1(pk);	/* output throttle */
	    break;

	case INP_BUF:
	    break;

	    } /*switch-Fmod*/

	return(q2device(pkt));		/* pass to device handler */


#ifdef DEBUG_MAP   /***********************************************************/
    case VDMAPPOOL:
#endif /* DEBUG_MAP ***********************************************************/

	break;

    case PDCREATE:
	Recs_rlength(dv) = Stataux2(pk);	/* set max record length */

	/* 4th status word contains device-dependent access flags */
	Recs_imode(dv) = ((flg = Stataux4(pk)) & (R_BLOCK|R_STREAM));

	if (flg & P_ECHO_OK)  Recs_echook(dv) = TRUE;
	if (flg & P_REWIND_OK)  Recs_rwdok(dv) = TRUE;
	if (flg & P_FDSPC_OK)  Recs_fdspcok(dv) = TRUE;
	if (flg & P_BKSPC_OK)  Recs_bkspcok(dv) = TRUE;

    case PDREMOVE:
	break;

    case VDINFO:			/* copy device info to user buffer */
	put_val(Recs_rlength(dv), pkt);	/* 1st word to copy */
	put_val(Recs_imode(dv), pkt);	/* 2nd word to copy */
	put_val(Recs_bytenum(dv), pkt);	/* 3rd word to copy */
	put_val(0, pkt);		/* 4th word to copy */
	break;


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

	} /*switch-cmd*/

    return(VIO_RUN_STATE);		/* continue processing */
}
