/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "src/dev/dasd/os2dasd/dmstrat2.c, dsdm, r206, 6a.585 93/07/15";*/
#define SCCSID  "src/dev/dasd/os2dasd/dmstrat2.c, dsdm, r206, 6a.585 93/07/15"

/**************************************************************************
 *
 * SOURCE FILE NAME = DMSTRAT2.C
 *
 * DESCRIPTIVE NAME = OS2DASD.DMD - OS/2 DASD Device Manager
 *                    Strategy 2 interface for OS/2 DASD Manager
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  Provides validation and routing of Strategy 2 requests
 *              received from the OS/2 Kernel and/or Installable File
 *              Systems.
 *
 *
*/
#include "dmh.h"
#include "dmfault.h"


/*---------------------------------------------------*/
/* Forward Function References and pragma statements */
/*---------------------------------------------------*/

VOID NEAR ValidateReqList (PReq_List_Header, NPVOLCB FAR *);
VOID ExecReqList (PReq_List_Header, NPUNITCB);


/*------------------------------------------------------------------------
;
;** DMStrat2 - Strategy2 Request List Entry Point
;
;   Processes Strategy2 request lists
;
;   USHORT DMStrat2    (PReq_List_Header pRLH)
;
;   ENTRY:    pRLH             - Request List Header
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID _loadds FAR DMStrat2 ()

{
   PReq_List_Header pRLH;
   NPVOLCB pVolCB;

   _asm { mov word ptr pRLH[0], bx };
   _asm { mov word ptr pRLH[2], es };

   pRLH->Lst_Status = 0;
   pRLH->y_Done_Count = 0;              /* Zero out request done count */

   /* Validate the request list */

   ValidateReqList(pRLH, (NPVOLCB FAR *) &pVolCB);

   /* If request list not aborted from validate, then execute the list */

   if ( !(pRLH->Lst_Status & RLH_All_Req_Done) )
   {
      if ( IsTraceOn() )
         Trace(TRACE_STRAT2 | TRACE_ENTRY, pRLH, pVolCB);

      pVolCB->Flags &= ~vf_ForceRdWrt;
      pRLH->y_PhysAddr = VirtToPhys((PBYTE)pRLH); /* Store phys addr of RLH */

      /* See if Fault Tolerance is enabled */

      if (DDFlags & DDF_FT_ENABLED)
         f_FT_ExecReqList (pRLH, pVolCB->pUnitCB);
      else
         ExecReqList (pRLH, pVolCB->pUnitCB);
   }
}

/*------------------------------------------------------------------------
;
;** ExecReqList - Execute Strategy2 Request List
;
;   Executes Strategy2 request lists
;
;   USHORT ExecReqList    (PReq_List_Header pRLHIn, pUnitCB);
;
;   ENTRY:    pRLHIn       - Request List Header
;             pUnitCB      - Pointer to UnitCB
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID ExecReqList (pRLHIn, pUnitCB)

PReq_List_Header pRLHIn;
NPUNITCB pUnitCB;
{
   USHORT  i, Count;

   _segment ReqSeg;
   Req_List_Header _based(ReqSeg) *pRLH;
   PB_Read_Write _based(ReqSeg) *pRLE;

   ReqSeg = SELECTOROF(pRLHIn);

   (USHORT) pRLH = OFFSETOF(pRLHIn);
   (USHORT) pRLE = (USHORT) pRLH + sizeof(Req_List_Header);

   /* Loop through the list */

   Count = pRLH->Count;
   for (i = 0; i < Count; i++)
   {
      if (pRLE->RqHdr.Status == RH_NOT_QUEUED)
      {
         PutPriorityQueue_RLE (pUnitCB, pRLE);

         /* Only queue one at a time if Execute in Sequence specified */

         if (pRLH->Request_Control & RLH_Exe_Req_Seq)
            break;
      }
      OFFSETOF(pRLE) = OFFSETOF(pRLE) + pRLE->RqHdr.Length;
   }

   pRLH->Lst_Status &= ~RLH_Req_Not_Queued;
   pRLH->Lst_Status |= RLH_All_Req_Queued;

   SubmitRequestsToADD(pUnitCB);
}


/*------------------------------------------------------------------------
;
;** ValidateReqList -  Validate a Strategy-2 request list
;
;   Verify the Request List Header has a valid unit number.  Verify
;   each Request List Entry has a valid command code and that the
;   I/O is within the partition boundaries.
;
;   USHORT ValidateReqList (PReq_List_Header pRLHIn, NPVOLCB *pVolCB_Addr)
;
;   ENTRY:    pRLHIn           - Request List Header
;             pVolCB_Addr      - returned pointer to VolCB
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID  ValidateReqList (pRLHIn, pVolCB_Addr)

PReq_List_Header pRLHIn;
NPVOLCB FAR *pVolCB_Addr;

{
   USHORT  rc, i, Count;
   NPVOLCB pVolCB;

   _segment ReqSeg;
   Req_List_Header _based(ReqSeg) *pRLH;
   PB_Read_Write _based(ReqSeg) *pRLE;

   ReqSeg = SELECTOROF(pRLHIn);
   (USHORT) pRLH = OFFSETOF(pRLHIn);

   /* First validate the Request List Header.  Make sure the header    */
   /* contains a valid unit number.  If the unit is for a floppy drive,*/
   /* make sure the floppy disk doesnt need to be changed.             */

   rc = 0;
   if (Get_VolCB_Addr(pRLH->Block_Dev_Unit, (NPVOLCB FAR *) pVolCB_Addr)
            == ERROR)

      rc = STDON + STERR + ERROR_I24_BAD_UNIT;

   else
   {
      pVolCB = *pVolCB_Addr;

      /*--------------------------------------------------------------*/
      /*  If it's for a psuedo-drive which is not currently mapped to */
      /*  it's associated unit, then a disk swap is needed.           */
      /*--------------------------------------------------------------*/

      if  (pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
      {
         if (CheckPseudoChange(pRLH->Block_Dev_Unit, pVolCB) == -1)
            rc = STDON + STERR + ERROR_I24_DISK_CHANGE;

         else if  ( (!(pVolCB->Flags & vf_ForceRdWrt)) &&
              (pVolCB->Flags & vf_UncertainMedia) )
         {
            rc = STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA;
         }
      }
   }

   /* If the Request List Header had an error, then return an error for  */
   /* all entries in the list.                                           */

   if (rc & STERR)
   {
      pRLH->Lst_Status |= RLH_Unrec_Error;

      (USHORT) pRLE = (USHORT) pRLH + sizeof(Req_List_Header);
      Count = pRLH->Count;
      for (i = 0; i < Count; i++)
      {
         pRLE->Blocks_Xferred = 0;
         pRLE->RqHdr.Status = RH_UNREC_ERROR;
         pRLE->RqHdr.Error_Code = (UCHAR) rc;
         NotifyRLE(pRLE);
         OFFSETOF(pRLE) = OFFSETOF(pRLE) + pRLE->RqHdr.Length;
      }
      return;
   }

   /* The Request List Header is ok. Make sure each request in the  */
   /* list has a valid command code and the I/O fails within the    */
   /* partition boundary.                                           */

   (USHORT) pRLE = (USHORT) pRLH + sizeof(Req_List_Header);
   Count = pRLH->Count;

   for (i = 0; i < Count; i++)
   {
      rc = 0;

      /* Copy the Block_Dev_Unit from the RLH to each RLE   */
      ((PRHFT)pRLE)->Block_Dev_Unit = pRLH->Block_Dev_Unit;
      ((PRHFT)pRLE)->ftdb.FT_Flags = 0;

      if (pRLE->RqHdr.Command_Code == PB_READ_X ||
          pRLE->RqHdr.Command_Code == PB_WRITE_X ||
          pRLE->RqHdr.Command_Code == PB_WRITEV_X)
        ;
      else if (pRLE->RqHdr.Command_Code == PB_PREFETCH_X)
      {
         if ( !(pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_PREFETCH) )
            rc = ERROR_I24_BAD_COMMAND;
      }
      else
         rc = ERROR_I24_BAD_COMMAND;

      if (rc == 0)
      {
         if (CheckWithinPartition(pVolCB, pRLE->Start_Block,
                                            pRLE->Block_Count) & STERR)
            rc = ERROR_I24_SECTOR_NOT_FOUND;
      }

      /* If the Request List Entry had an error notify the file system */

      if (rc != 0)
      {
         pRLE->RqHdr.Status = RH_UNREC_ERROR;
         pRLE->RqHdr.Error_Code = (UCHAR) rc;
         pRLE->Blocks_Xferred = 0;
         NotifyRLE(pRLE);

         /* Abort the list and return if Abort_Err specified */

         if (pRLH->Request_Control & RLH_Abort_Err)
         {
            AbortReqList(pRLH);
            return;
         }
      }
      OFFSETOF(pRLE) = OFFSETOF(pRLE) + pRLE->RqHdr.Length;
   } /* end for */
}

/*------------------------------------------------------------------------
;
;** AbortReqList -  Abort the request list
;
;   Abort the reqeust list when Abort on Error is specified.
;
;   VOID AbortReqList (PReq_List_Header pRLHIn)
;
;   ENTRY:    pRLH             - Request List Header
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID AbortReqList (pRLHIn)

PReq_List_Header pRLHIn;

{
   USHORT i, Count;

   _segment ReqSeg;
   Req_List_Header _based(ReqSeg) *pRLH;
   PB_Read_Write _based(ReqSeg) *pRLE;

   ReqSeg = SELECTOROF(pRLHIn);
   (USHORT) pRLH = OFFSETOF(pRLHIn);

   pRLH->Lst_Status |= RLH_Unrec_Error;

   (USHORT) pRLE = (USHORT) pRLH + sizeof(Req_List_Header);
   Count = pRLH->Count;

   for (i = 0; i < Count; i++)
   {
      if ( !(pRLE->RqHdr.Status & RH_DONE) )
      {
         pRLE->Blocks_Xferred = 0;
         pRLE->RqHdr.Status |= RH_ABORTED;
         pRLE->RqHdr.Error_Code = 0;
         NotifyRLE(pRLE);
      }
      OFFSETOF(pRLE) = OFFSETOF(pRLE) + pRLE->RqHdr.Length;
   }
}


/*------------------------------------------------------------------------
;
;** NotifyRLE - Notification callout for Request Lists
;
;   Perform notification callouts to the filesystem when a
;   Request List Entry is done.  Also, do the final Request List
;   notification callout when the entire list is complete.
;
;   USHORT NotifyRLE (PB_Read_Write pRLEIn)
;
;   ENTRY:    pRLEIn           - Request List Entry
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID NotifyRLE (pRLEIn)

PPB_Read_Write pRLEIn;

{
   _segment ReqSeg;
   Req_List_Header _based(ReqSeg) *pRLH;
   PB_Read_Write _based(ReqSeg) *pRLE;

   ReqSeg = SELECTOROF(pRLEIn);
   (USHORT) pRLE = OFFSETOF(pRLEIn);

   /* Make sure this request is not already done */

   DISABLE;
   if (pRLE->RqHdr.Status & (RH_DONE | RH_ABORTED) )
   {
      ENABLE;
      return;
   }

   /* Set the status field to RH_DONE and do notification callouts */
   /* if required.                                                 */

   pRLE->RqHdr.Status |= RH_DONE;

   ENABLE;

   pRLH = (PReq_List_Header) pRLE;
   OFFSETOF(pRLH) = OFFSETOF(pRLE) - (USHORT) pRLE->RqHdr.Head_Offset;

   /* If there's an error, see if we should notify on errors */

   if ( (pRLE->RqHdr.Status & RH_RECOV_ERROR) &&
        ( !(pRLH->Lst_Status & RLH_Unrec_Error) ) )
      pRLH->Lst_Status |= RLH_Rec_Error;

   if (TraceFlags != 0)                                              /*@V85908*/
      Trace((USHORT) (TRACE_STRAT2 | TRACE_ASYNCDONE | TRACE_RLE),   /*@V85908*/
            (PBYTE)   pRLE,                                          /*@V85908*/
            (NPVOLCB) 0);                                            /*@V85908*/

   if (pRLE->RqHdr.Status & RH_UNREC_ERROR)
   {
     /* Notify with error */

      pRLH->Lst_Status |= RLH_Unrec_Error;
      if (pRLE->RqHdr.Req_Control & (RH_NOTIFY_ERROR | RH_NOTIFY_DONE) )
         FSD_Notify((PBYTE)pRLE, pRLE->RqHdr.Notify_Address, 1);
   }
   else if (pRLE->RqHdr.Req_Control &  RH_NOTIFY_DONE)

         /* Notify with no with error */

         FSD_Notify((PBYTE)pRLE, pRLE->RqHdr.Notify_Address, 0);

   /* Increment the requests completed count in the request list header. */
   /* If all the requests in the list are done, or an error occurred,    */
   /* then call the list notification routine if required.               */

   pRLH->y_Done_Count ++;

   if (pRLH->y_Done_Count == pRLH->Count)
   {                                                                 /*@V85908*/
      pRLH->Lst_Status &= ~(RLH_Req_Not_Queued | RLH_All_Req_Queued);
      pRLH->Lst_Status |= RLH_All_Req_Done;

      if (TraceFlags != 0)                                           /*@V85908*/
         Trace((USHORT) (TRACE_STRAT2 | TRACE_ASYNCDONE),            /*@V85908*/
               (PBYTE)   pRLH,                                       /*@V85908*/
               (NPVOLCB) 0);                                         /*@V85908*/

      if (pRLH->Request_Control & RLH_Notify_Done)
      {
         /* List notify when done */

         FSD_Notify((PBYTE)pRLH,                                     /*@V71660*/
                           pRLH->Notify_Address,
                           (pRLH->Lst_Status & RLH_Unrec_Error) ? 1 : 0 );

      }
      else if ( (pRLH->Lst_Status & RLH_Unrec_Error) &&              /*@V71660*/
                (pRLH->Request_Control & RLH_Notify_Err) )
      {
            /* List notify on error */

         FSD_Notify((PBYTE)pRLH, pRLH->Notify_Address, 1);
      }
   }                                                                 /*@V85908*/
}


/*------------------------------------------------------------------------
;
;** DD_ChgPriority - Change the priority of a Request List Entry
;
;   Change the priority of a Request List Entry
;
;   USHORT DD_ChgPriority   (PPB_ReadWrite pRLE, UCHAR Priority)
;
;   ENTRY:    pRLE             - Request List Entry
;             Priority         - Priority
;
;   RETURN:   USHORT           - Return Code
;                                  0 = No error, priority changed
;                                  1 = Error, priority not changed since
;                                      request no longer on queue
; ------------------------------------------------------------------------*/
USHORT  DD_ChgPriority (pRLE, Priority)

PPB_Read_Write pRLE;
UCHAR  Priority;

{
   USHORT rc;
   PReq_List_Header pRLH;
   NPVOLCB pVolCB;

   /* If same priority as before then no need to change */

   if (pRLE->RqHdr.Priority == Priority)
      rc = 0;
   else if (pRLE->RqHdr.Status == RH_NOT_QUEUED)
   {
      pRLE->RqHdr.Priority = Priority;
      rc = 0;
   }
   else if (pRLE->RqHdr.Status != RH_QUEUED)
      rc = 1;

   else   /* Request is already queued.  Move to new Priority queue. */
   {
      pRLH = (PReq_List_Header) pRLE;
      OFFSETOF(pRLH) = OFFSETOF(pRLE) - (USHORT) pRLE->RqHdr.Head_Offset;
      Get_VolCB_Addr(pRLH->Block_Dev_Unit, (NPVOLCB FAR *)&pVolCB);
      if (RemovePriorityQueue(pVolCB->pUnitCB, pRLE) == NO_ERROR)
      {
         pRLE->RqHdr.Priority = Priority;
         PutPriorityQueue_RLE (pVolCB->pUnitCB, pRLE);
         rc = 0;
      }
      else
         rc = 1;
   }

   return(rc);
}

/*------------------------------------------------------------------------
;
;** DD_SetFSDInfo - Set FSD callout addresses.
;
;   Entry point for FSD to inform us of it's FSD_EndofInt and
;   FSD_AccValidate entry points.
;
;   USHORT DD_SetFSDInfo  (PSet_FSD_Info pFSDInfo)
;
;   ENTRY:    pFSDInfo         - FSD info structure
;
;   RETURN:   USHORT           - Return Code
;                                  0 = No error, FSD info changed
;                                  1 = Error, FSD info not changed
; ------------------------------------------------------------------------*/
USHORT _loadds FAR DD_SetFSDInfo ()

{
   FSDInfo FAR *pFSDInfo;
   USHORT rc;

   _asm { mov word ptr pFSDInfo[0], bx };
   _asm { mov word ptr pFSDInfo[2], es };

   /* Can only set FSDInfo callout addresses once. */

   if (pFSD_EndofInt != 0 || pFSD_AccValidate != 0)
   {
      rc = 1;
      _asm {stc};
   }
   else
   {
      pFSD_EndofInt = pFSDInfo->FSD_EndofInt;
      pFSD_AccValidate = pFSDInfo->FSD_AccValidate;
      rc = 0;
      _asm {clc};
   }

   return(rc);
}





