/*static char *SCCSID = "@(#)fs_dopio.c 6.4 92/01/31";*/

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <..\..\dos\dosinc\osconst.h>
#include <fsd.h>
#include <strat2.h>

#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "assert.h"
#include "pb_ifs.h"
#include "pbmacros.h"
#include "frame.h"
#include "asyncio.h"
#include "pbmsg.h"
#include "lw.h"
#include "fsdata.h"
#include "heap.h"
#include "pr_dskio.h"
#include "pr_fssub.h"
#include "pr_HEAP.h"
#include "pr_misc.h"
#include "pr_READ.h"

#ifdef DEBUG
#include "pr_DEBUG.h"
#endif


/***    FS_DoPageIO - Performs paging I/O operations.
 *
 *      FS_DoPageIO performs all the I/O operations specified in the
 *      PageCmdList.
 *
 *      If the disk driver has a Strategy2 entry, a request list will be built
 *      from the PageCmdList and issued to the driver.
 *
 *      If the disk driver does not support Strategy2, the FSD can either let
 *      the kernel do the emulation (see FS_OpenPageFile to set this state) or
 *      has the option to do the emulation itself.
 *
 *      Currently HPFS will let the kernel emulate DoPageIO for old disk
 *      drivers.
 *
 *      All calls with be synchronous (all requests will be completed) before
 *      returning to the caller.  (Some requests may have received errors.)
 *
 *      ENTRY   psffsi      = File System Independent portion of SFT
 *              psffsd      = File System Dependent portion of SFT
 *              *pList      = PageCmdList containing requests
 *
 *      EXIT  Returns 0 if OK (all requests worked without error)
 *
 *            Returns non-zero error code if error (at least 1 request did not
 *                      work - caller must examine status field for each
 *                      operation to determine which requests worked)
 ***/

int far pascal
FS_DoPageIO (struct sffsi far *        psffsi,
             struct sffsd far *        psffsd,
             struct PageCmdHeader far *pList)

{
    int RetCode;
    POFT poft;
    PIOH pioh;
    struct PageCmd far *pOpList;

    PBYTE pbEventFlags;

    FRAME frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;
    frm.fFlag = 0;               // Clear Frame flag  @132506

    {   PFHT pfht;

        pfht = ((PPB_SFD)(psffsd->sfd_work))->pfht;
        poft = pfht->poft;

#ifdef DEBUG
        /*
         * If we want to do this assert, first we have to be sure it is not
         * locked.  HoldOFT does not lock it.  As long as ASSERT_OFT does not
         * block, it will work fine.
         */
        HoldOFT (poft,&frm);
        ASSERT_OFT( poft );
#endif

        /* We use offSeek to keep the PIOH pointer for this handle */
        pioh = (PIOH) pfht->offSeek;
#ifdef DEBUG
        if (pioh == NULL) {
            dprintf (__FILE__": pioh (pfht->offSeek) == NULL\n");
            Int3 ();
        }
#endif
    } /* end of scope for phft */

#ifdef DEBUG_ASYNC
    dprintf (__FILE__": pioh %p; pList %p; pList->OpCount %d\n",
             pioh, pList, pList->OpCount);
#endif
    {
        PVOLTAB pvol;

        frm.pvol = pvol = poft->pVolume;

        if (pvol->pfncStrategy2 == NULL) {
            /* we told the kernel we could not handle this request */

            /* we could later add emulation here */
            PANIC(PBMSG_DOPIO_NOCANDO);
        }

        /*
         * Set up pioh for this request list.  We are dealing with physical
         * addresses.
         *
         * Note: if PGIO_FI_ORDER is specified, GIH_INORDER & GIH_ABORTERR
         * are both specified.
        */

        ResetIOH (pioh, pvol->ulVolBits | GIH_PHYSADDR |
                  ((pList->InFlags & PGIO_FI_ORDER) ?
                                            GIH_INORDER | GIH_ABORTERR : 0L));

#ifdef DEBUG
        if (pList->OpCount > HPFS_MAXREQ) {
            dprintf (__FILE__": Pager OpCount (%d) > HPFS_MAXREQ (%d)\n",
                     pList->OpCount, HPFS_MAXREQ);
            Int3 ();
        }

        if (pvol->csgMax) {
            if ((USHORT) pList->OpCount > pvol->csgMax) {
                dprintf (__FILE__": Pager OpCount (%d) > pvol->csgMax (%d)\n",
                         pList->OpCount, pvol->csgMax);
                Int3 ();
            }
        }
#endif
    } /* end of scope for pvol */

    /* set the gobal status */
    pList->OutFlags = 0;

    {   UCHAR iPageCmd;
        LSN lsn;
        ULONG ulContig;

        for (iPageCmd = 0; iPageCmd < pList->OpCount; iPageCmd++) {
            pOpList = &pList->PageCmdList [iPageCmd];

            pOpList->Status = PGIO_ATTEMPTED;

            lsn = CPS(pOpList->FileOffset, poft, &ulContig, &frm);
            if (lsn == LSN_BOGUS) {
                /* hard error or corrupted file - return failure */
                RetCode = ERROR_GEN_FAILURE;
                goto piodone;
            }
#ifdef DEBUG
            if (ulContig < PAGESIZE / SECSIZE) {
                dprintf (__FILE__": ulContig (%d) < PAGESIZE (%d)\n",
                         ulContig, PAGESIZE);
                Int3 ();
            }
#endif

/***** TEMPORARY *****/
/*
 * This code is necessary until the kernel & pager switch to the new commands.
 */
/* these defines are from h\devcmd.h */
#define CMDINPUT                4       /* read data from device */
#define CMDOUTPUT               8       /* write data to device */

            if (pOpList->Cmd == CMDINPUT) {
                pOpList->Cmd = PB_READ_X;
            } else if (pOpList->Cmd == CMDOUTPUT) {
                pOpList->Cmd = PB_WRITE_X;
            }
/***** TEMPORARY *****/

#ifdef DEBUG_ASYNC
            dprintf (__FILE__": iOp %d; lsn %lx, pData %lx, Cmd %c\n",
                     (USHORT) iPageCmd, lsn, pOpList->Addr,
                     ((pOpList->Cmd == PB_READ_X) ? 'R' : 'W'));
#endif

       pbEventFlags = 0;
            /* add this request to pioh */
//#83284
//          RetCode = QDiskOp (pioh, pOpList->Priority,
//
            RetCode = QDiskOp (pioh, PRIO_BACKGROUND_USER,
                               pOpList->Cmd | QDO_RET_HOTFIX , lsn,
                               PAGESIZE / SECSIZE, (PBYTE) pOpList->Addr,
                               pbEventFlags, &frm );

            if (RetCode) {
                if (RetCode == QDERR_HOTFIX) {
                    /* At least one sector was already hotfixed. */
                    pOpList->Status |= RH_RECOV_ERROR;
                    pList->OutFlags |= PGIO_FO_ERROR;
#ifdef DEBUG_ASYNC
                    dprintf (__FILE__": QDiskOp returned HotFix (%d); pioh %p\n",
                             RetCode, pioh);
                    Int3 ();
#endif
                } else {
#ifdef DEBUG
                    dprintf (__FILE__": QDiskOp returned error %d; pioh %p\n",
                             RetCode, pioh);
                    while (TRUE) {
                        Int3 ();
                    }
#else
                    /* should not see any errors - list should be big enough */
                    PANIC(PBMSG_DOPIO_QDISKOP_ERROR);
#endif
                }
            }
        }
    }

   /* identify time of last IO for the lazy writer */
    msLastIO = msNow ();

    /* issue the I/O request - wait for it to finish */
    RetCode = ExecIOH (pioh, TRUE, &frm);

#ifdef DEBUG_ASYNC
    dprintf (__FILE__": post-ExecIOH; RetCode %d; &RetCode ss:%x; pioh %p\n",
             RetCode, &RetCode, pioh);
    Int3 ();
#endif

    if (RetCode) {

        UCHAR iPageCmd;
        LSN lsn;

        /* iterate through entire list, 1 page at a time */
        for (iPageCmd = 0; iPageCmd < pList->OpCount; iPageCmd++) {
            /* set up pioh for this request */
            ResetIOH (pioh, pioh->pvol->ulVolBits | GIH_PHYSADDR);

            pOpList = &pList->PageCmdList [iPageCmd];

            {   ULONG ulContig;

                lsn = CPS(pOpList->FileOffset, poft, &ulContig, &frm);
            }

#ifdef DEBUG_ASYNC
            dprintf (__FILE__": retrying # %d; pioh %p\n", iPageCmd, pioh);
            Int3 ();
#endif

       pbEventFlags = 0;
            /* add this request to pioh */
//#83284
//          RetCode = QDiskOp (pioh, pOpList->Priority, pOpList->Cmd, lsn,
//
            RetCode = QDiskOp (pioh, PRIO_BACKGROUND_USER, pOpList->Cmd, lsn,
                               PAGESIZE / SECSIZE, (PBYTE) pOpList->Addr,
                               pbEventFlags, &frm );

            if (RetCode) {
                /* should not see any error */
                PANIC(PBMSG_DOPIO_QDISKOP_ERROR);
            }

            /* issue the I/O request - wait for it to finish */
            RetCode = ExecIOH (pioh, TRUE, &frm);

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

                if (pOpList->Cmd == PB_READ_X) {
                    /* move error from LISTENT to PageCmd */
                    pOpList->Error = ple->RWReq.RqHdr.Error_Code;

                    /* mark LISTENT.Status as error */
                    pOpList->Status |= PGIO_FAILED;

                    /* mark PageCmdHeader.OutFlags as error */
                    pList->OutFlags |= PGIO_FO_ERROR;
                } else {    /* else write */
                    UINT iSec;

                    pList->OutFlags |= PGIO_FO_ERROR;
                    pOpList->Status |= RH_UNREC_ERROR;

                    /* move error from LISTENT to PageCmd */
                    pOpList->Error = ple->RWReq.RqHdr.Error_Code;

                    /* hotfix */

                    /*  We have to iterate through all 8 sectors with a
                     *  dummy buffer since we don't have a virtual address
                     *  for the page frame.  The intent here is to get any
                     *  bad sectors marked bad, so that ChkDsk can later
                     *  convert them to bad sectors.
                     */
                    for (iSec = 0; iSec < PAGESIZE / SECSIZE; iSec++) {
                        WriteHF ((lsn + iSec) | pioh->pvol->ulVolBits,
                                 1, abHotFixBuf,&frm);
                    } /* for */

                    /*  Since we are returning the error anyway, there is no
                     *  need to rewrite the original page frame.
                     */
                }

                if (pList->InFlags & PGIO_FI_ORDER) {
                    /* in-order implies abort on error  -
                     * clear attempted bit in rest of page command list
                     */
                    for (iPageCmd++; iPageCmd < pList->OpCount; iPageCmd++){
                        pList->PageCmdList [iPageCmd].Status = 0;
                    }

                    break;
                }
            }
        }

#ifdef DEBUG_ASYNC
        dprintf (__FILE__": Error handling completed; pList %p\n", pList);
        Int3 ();
#endif
    }

    /* set global status (completed) */
    pList->OutFlags |= PGIO_FO_DONE;

    RetCode = NO_ERROR;

piodone:

    return RetCode;

    /* to avoid compiler warning */
    psffsi;

}   /* FS_DoPageIO */
