/*static char *SCCSID = "@(#)iolist.c	6.2 91/09/19";*/
/*static char *SCCSID = "@(#)iolist.c	6.2 91/09/19";*/
#include <os2def.h>
#define INCL_DOSERRORS
#include <bse.h>
#include <fsd.h>
#include <fsh.h>
#include <strat2.h>

#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "heap.h"
#include "sgtest.h"
#include "asyncio.h"

#include "pr_DEBUG.h"
#include "pr_heap.h"
#include "pr_MISC.h"

/***	IOList - test/debug support for async I/O device driver
 *
 *	IOList supports various operations dealing with a device driver which
 *	exports a Strategy 2 entry.  These operations deal with scatter/gather
 *	and chained I/O operations.
 *
 *	ENTRY
 *
 *	EXIT
 */

/***	These definitions concern the device driver interface ***/

    #define GETDEVICESUPPORT 0x1D

    struct REQPKT0 {
	BYTE   ReqLen;		    /* Length in bytes of request block       */
	BYTE   ReqUnit; 	    /* Device unit number		      */
	BYTE   ReqFunc; 	    /* Type of request			      */
	USHORT ReqStat; 	    /* Status Word			      */
	ULONG  Dummy1;		    /* RESERVED 			      */
	ULONG  DevLink; 	    /* link field for use by driver to chain  */
				    /*	 requests.  (A service in DevHelp)    */
	BYTE   Dummy2 [3];
	PVOID  pDCS;
	PVOID  pVCS;
    };

    typedef struct REQPKT0 REQPKT0;
    typedef REQPKT0 far *PREQPKT0;

    struct DPB {
	BYTE dpb_drive;
	BYTE dpb_unit;
	PVOID dpb_driver_addr;
	ULONG dpb_next_dpb;
	USHORT dpb_cbSector;
	USHORT dpb_first_FAT;
	ULONG dpb_toggle_time;
	USHORT dpb_hVPB;
	BYTE dpb_media;
	BYTE dpb_flags;
	USHORT dpb_drive_lock;
    };

    typedef struct DPB DPB;
    typedef DPB far *PDPB;

int far
IOList (USHORT	hVPB,
	USHORT	sfn,
	POFT	poft,
	PCHAR	pcParm, 	    /* pointer to parameter packet */
	USHORT	usLenParm,	    /* length of parameter packet */
	PUSHORT pusLenParmOut,	    /* pointer to returned param length */
	PCHAR	pbData, 	    /* pointer to data area */
	USHORT	usLenData,	    /* length of data area */
	PUSHORT pusLenDataOut)	    /* pointer to returned data length */

{
    PVOLTAB pVol;
    USHORT usRet;
    PSGTEST_PP pSGTest;
    PDPB pDPB;
	 ULONG pEventFlags = 0;

    /* get pointer to current volume */
    if ((pVol = FindPVol (hVPB)) == NULL) {
	return ERROR_INVALID_HANDLE;
    }

    /* Make sure the param pointer is valid */
    if (usLenParm < sizeof (SGTEST_PP)) {
	*pusLenParmOut = sizeof (SGTEST_PP);
	return ERROR_BAD_LENGTH;
    }

    /* probe the address of where the parameter block is stored */
    MAYBLOCK (
	usRet = FSH_PROBEBUF (PB_OPREAD,
			      pcParm,
			      sizeof (SGTEST_PP)););
    if (usRet != NO_ERROR) {
	return usRet;
    }

    {	struct vpfsi far   *pVPBfsi;
	struct vpfsd far   *pVPBfsd;

	/* get volume parameters */
	usRet = FSH_GETVOLPARM (hVPB, (struct vpfsi far * far *) &pVPBfsi,
				      (struct vpfsd far * far *) &pVPBfsd);
	pDPB = (PDPB) pVPBfsi->vpi_hDEV;
    }

    pSGTest = (PSGTEST_PP) pcParm;

    switch (pSGTest->usSubFunct) {

    case SGTEST_SF_GSUP:
	{
	    PREQPKT0 pReqPkt;

	    if ((pReqPkt = GHS (sizeof (REQPKT0))) == NULL) {
		usRet = ERROR_NOT_ENOUGH_MEMORY;
		break;
	    }

	    pReqPkt->ReqLen = sizeof (REQPKT0);
	    pReqPkt->ReqUnit = pDPB->dpb_unit;
	    pReqPkt->ReqFunc = GETDEVICESUPPORT;
	    pReqPkt->DevLink = 0;
	    pReqPkt->pDCS = pSGTest->pp_misc.pp_type_0.pbT0DCS;
	    pReqPkt->pVCS = pSGTest->pp_misc.pp_type_0.pbT0VCS;

	    CallDD (pDPB->dpb_driver_addr, (PVOID) pReqPkt);

	    BYTE_COPY (pReqPkt->pDCS,
		       pSGTest->pp_misc.pp_type_0.pbT0DCS,
		       sizeof (DriverCaps));

	    BYTE_COPY (pReqPkt->pVCS,
		       pSGTest->pp_misc.pp_type_0.pbT0VCS,
		       sizeof (VolChars));

	    pSGTest->pp_misc.pp_type_0.usT0DriverRetCode = pReqPkt->ReqStat;

	    RetHeap (pReqPkt);

	    /* remember the unit number */
	    poft->pVolume->idLogDisk = pDPB->dpb_unit;

	    Reachable (NULL);

	    sfn;
	}   /* scope end for pReqPkt */

	break;

    case SGTEST_SF_INIT:
	poft->pVolume->pfncStrategy2 =
			(PFN)(ULONG)(pSGTest->pp_misc.pp_type_1.pStrategy2);
	usRet = NO_ERROR;

	Reachable (NULL);

	break;

    case SGTEST_SF_READ:
	{
	    PIOH pioh;

	    if (usLenData < pSGTest->pp_misc.pp_type_2.csData * SECSIZE) {
		dprintf ("%s (%d): usLenData (%d) < csData (%d) * SECSIZE\n",
			 __FILE__ ,__LINE__, usLenData,
			 pSGTest->pp_misc.pp_type_2.csData);
	    }

	    pioh = GetIOHand (poft->pVolume->ulVolBits | GIH_ABORTERR, 4, 1);

	    Reachable (NULL);

	    usRet = QDiskOp (pioh, 0x8, PB_READ_X,
			     pSGTest->pp_misc.pp_type_2.ulLSN,
			     pSGTest->pp_misc.pp_type_2.csData,
			     pbData, pEventFlags );

	    Reachable (NULL);

	    if (usRet > QDERR_AUTOFLUSH) {
		dprintf (__FILE__" (%d): QDiskOp failed (%x)\n",
			 __LINE__, usRet);
		Int3 ();
		break;
	    }

	    if (IsAsyncIOable (poft->pVolume->ulVolBits)) {
		usRet = ExecIOH (pioh, TRUE);

		Reachable (NULL);

		if (usRet > QDERR_AUTOFLUSH) {
		    dprintf (__FILE__" (%d): ExecIO failed (%x)\n",
			     __LINE__, usRet);
		    Int3 ();
		    break;
		}

		{   PLISTENT ple = FirstIOReq (GetIOListHead (pioh));

		    pSGTest->pp_misc.pp_type_2.usT2DriverRetCode =
		       (ple->RWReq.RqHdr.Error_Code << 16) +
		       ple->RWReq.RqHdr.Status;
		}
	    } else {
		dprintf (__FILE__": IsAsyncIOable returned FALSE\n");
		usRet = ~NO_ERROR;
	    }

	    UnlockIOHPages (pioh);

	    Reachable (NULL);

	    RelIOHand (pioh);

	    Reachable (NULL);

	    *pusLenDataOut = pSGTest->pp_misc.pp_type_2.csData * SECSIZE;
	}
	break;

    default:
	usRet = ERROR_INVALID_FUNCTION;
    }

    return usRet;
}
