/*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/dmfault.c, dsdm, ddk_subset, b_bdd.032 93/03/19";*/
#define SCCSID  "src/dev/dasd/os2dasd/dmfault.c, dsdm, ddk_subset, b_bdd.032 93/03/19"

/**************************************************************************
 *
 * SOURCE FILE NAME = DMFAULT.C
 *
 * DESCRIPTIVE NAME = OS2DASD.DMD - OS/2 DASD Device Manager
 *
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Drive Mirroring Support for Fault Tolerant Lan
 *               Subsystem
 *
 *
*/

#include "dmh.h"
#include "dmfault.h"


VOID   FT_PutDeferredQueue  (PBYTE);                                 /*@V99999*/
#pragma alloc_text(SwapCode, FT_PutDeferredQueue)                    /*@V99999*/

USHORT   FT_PullDeferredQueue (PBYTE FAR *);                         /*@V99999*/
#pragma alloc_text(SwapCode, FT_PullDeferredQueue)                   /*@V99999*/

VOID   FT_PutDeferredRequest (PBYTE);                                /*@V99999*/
#pragma alloc_text(SwapCode, FT_PutDeferredRequest)                  /*@V99999*/

USHORT FT_LockFTCode(VOID);
#pragma alloc_text(SwapCode, FT_LockFTCode)

VOID   FT_CodeSegEnd(VOID);
#pragma alloc_text(SwapCode, FT_CodeSegEnd)

/*------------------------------------------------------------------------
;
;** GIO_FaultTolerance - IOCTL to enable fault tolerance support
;
;   IOCTL to enable fault tolerance support
;
;   USHORT GIO_RWVTrack (NPCWA pCWA)
;
;   ENTRY:    pCWA             - CWA
;
;   RETURN:   USHORT           - Packet Status word
;
------------------------------------------------------------------------*/
USHORT GIO_FaultTolerance (pCWA)

NPCWA pCWA;
{
   PFT_IOCTL_param pParmPkt;
   PPDT   pPDT;
   USHORT TotalPartitions, i, rc;
   NPVOLCB pVolCB;

   if ((rc = CheckFTPacket(pCWA)) & STDON)
      return(rc);

   pParmPkt = (PFT_IOCTL_param) pCWA->pParmPkt;
   pPDT = pParmPkt->pPDT;

   TotalPartitions = NumPartitions + NumFTPartitions;


   /* Fill in the partition descriptor table header */

   ((PPDTH)pPDT)->NumControllers = (UCHAR) NumAdapters;
   ((PPDTH)pPDT)->NumPhysDisks =  (UCHAR) NumFixedDisks;
   ((PPDTH)pPDT)->NumPartitions = (UCHAR) TotalPartitions;
   ((PPDTH)pPDT)->NumLogUnits = (UCHAR) NumPartitions;
   ((PPDTH)pPDT)->PartNumOffset = 0;
   ((PPDTH)pPDT)->Reserved_1 = 0;
   ((PPDTH)pPDT)->Reserved_2 = 0;
   ((PPDTH)pPDT)->Reserved_3 = 0;
   ((PPDTH)pPDT)->Reserved_4 = 0;


   /* Fill in the partition decriptor entries for each logical drive */

   OFFSETOF (pPDT) = OFFSETOF (pPDT) + sizeof(PDTH);

   pVolCB = pVolCB_DriveC;

   for (i = 0; i < TotalPartitions; i++, pVolCB = pVolCB->pNextVolCB, pPDT++)
   {
      pPDT->Controller = (UCHAR) pVolCB->pUnitCB->AdapterNumber;
      pPDT->PhysDisk = (UCHAR) pVolCB->pUnitCB->PhysDriveNum;
      pPDT->PartitionType = pVolCB->PartitionType;
      pPDT->LogUnit = (UCHAR) pVolCB->LogDriveNum;
      if (pVolCB->Flags & vf_FTPartition)
         pPDT->LogUnit = -1;

      pPDT->StartSec = pVolCB->PartitionOffset+pVolCB->MediaBPB.HiddenSectors;

      if (pVolCB->MediaBPB.TotalSectors != 0)
         pPDT->EndSec = pPDT->StartSec + pVolCB->MediaBPB.TotalSectors - 1;
      else
         pPDT->EndSec = pPDT->StartSec + pVolCB->MediaBPB.BigTotalSectors - 1;

      pPDT->Reserved = 0;

      pVolCB->FT_PartitionNumber = i + 1;
   }


   /* Fill in the partition decriptor entries for each physical drive */

   for (i = 0; i < NumFixedDisks; i++, pVolCB = pVolCB->pNextVolCB, pPDT++)
   {
      pPDT->Controller = (UCHAR) pVolCB->pUnitCB->AdapterNumber;
      pPDT->PhysDisk = (UCHAR) pVolCB->pUnitCB->PhysDriveNum;
      pPDT->PartitionType = 0;
      pPDT->LogUnit = 0;
      pPDT->StartSec = 0;
      pPDT->EndSec = 0;
      pPDT->Reserved = 0;
   }

   if (pParmPkt->Command == FT_ENABLE)
   {
      pDiskFT_Request = (PVOID) pParmPkt->FT_Request;
      pDiskFT_Done = (PVOID) pParmPkt->FT_Done;
      DiskFT_DS = pParmPkt->FT_ProtDS;
      DDFlags |= DDF_FT_ENABLED;
   }
   else if (pParmPkt->Command == FT_DISABLE)
      DDFlags &= ~DDF_FT_ENABLED;

}

/*------------------------------------------------------------------------
;
;** CheckFTPacket - Check Fault Tolerance packets
;
;   USHORT CheckFTPacket (NPCWA pCWA)
;
;   ENTRY:    pCWA             - CWA
;
;   RETURN:   USHORT           - Packet Status word
;
------------------------------------------------------------------------*/
USHORT CheckFTPacket (pCWA)

NPCWA pCWA;
{
   USHORT rc, MaxPDTsize;
   PFT_IOCTL_param pParmPkt;
   PFT_IOCTL_data pDataPkt;
   PPDT pPDT;

   pParmPkt = (PFT_IOCTL_param) pCWA->pParmPkt;
   pDataPkt = (PFT_IOCTL_data) pCWA->pDataPkt;

   /* Verify access to the data and parameter packets */

   if ((rc = LockUserPacket(pCWA, LOCK_DATAPKT + LOCK_VERIFYONLY,
                            sizeof(FT_IOCTL_data))) & STERR)

      return(rc);


   if ((rc = LockUserPacket(pCWA, LOCK_PARMPKT + LOCK_VERIFYONLY,
                            sizeof(FT_IOCTL_param))) & STERR)

      return(rc);


   /* Lock the FT Code in the swap segment if FT not already locked */

   if ( !(DDFlags & DDF_FT_LOCKED) )
   {
      if ((rc = FT_LockFTCode()) & STERR)
         return(rc);
      else
         DDFlags |= DDF_FT_LOCKED;
   }

   /* Verify Access to the data block for the partition table         */
   /* Max number of PDT entries is: 1 for PDT header + max 24 logical */
   /* drives + number of fixed disks.                                 */

   pPDT = pParmPkt->pPDT;

   MaxPDTsize = (1 + 24 + NumFixedDisks) * sizeof (PDT);

   if (DevHelp_VerifyAccess
           (SELECTOROF(pPDT), MaxPDTsize, OFFSETOF(pPDT), 1) != 0)

      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);


   /* Check the signature, version and if any FT partitions exist */

   pDataPkt->FT_SigDD = FT_SIG_DD;

   if (pParmPkt->FT_SigFT != FT_SIG)
   {
      pDataPkt->SupportCode = FT_INVALID_SIGNATURE;
      return(STDON);
   }

   if (pParmPkt->FT_Version != FT_VERSION)
   {
      pDataPkt->SupportCode = FT_VERSION_INCOMPAT;
      return(STDON);
   }

   if (NumFTPartitions == 0)
      pDataPkt->SupportCode = FT_NO_FT_PARTITIONS;

   else
      pDataPkt->SupportCode = FT_SUPPORTED;

   return(0);
}

/*------------------------------------------------------------------------
;
;** FT_ExecReqList - Fault Tolerance Execute Strategy2 Request List
;
;   Executes Strategy2 request lists
;
;   USHORT FT_ExecReqList    (PReq_List_Header pRLH, pUnitCB);
;
;   ENTRY:    pRLH         - Request List Header
;             pUnitCB      - Pointer to UnitCB
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/
VOID FAR f_FT_ExecReqList (pRLH, pUnitCB)

PReq_List_Header pRLH;
NPUNITCB pUnitCB;
{
   FT_ExecReqList (pRLH, pUnitCB);
}

VOID NEAR FT_ExecReqList (pRLH, pUnitCB)

PReq_List_Header pRLH;
NPUNITCB pUnitCB;
{
   USHORT  i;
   PPB_Read_Write pRLE;
   NPUNITCB pUnitCB1  = 0;
   NPUNITCB pUnitCB2  = 0;
   NPUNITCB pUnitCB1t = 0;
   NPUNITCB pUnitCB2t = 0;

   (PReq_List_Header) pRLE = pRLH + 1;

   pUnitCB1 = pUnitCB;

   /* Loop through the list */

   for (i = 0; i < pRLH->Count; i++)
   {
      if (pRLE->RqHdr.Status == RH_NOT_QUEUED)
      {
         if (FT_CheckFTSupport(pUnitCB, (PBYTE) pRLE) == NO)
         {
             f_PutPriorityQueue_RLE (pUnitCB, pRLE);
             pUnitCB1 = pUnitCB;
         }
         else
         {
            FT_PutPriorityQueue(pUnitCB, (PBYTE) pRLE,
                     (NPUNITCB FAR *) &pUnitCB1t, (NPUNITCB FAR *) &pUnitCB2t);

            if (pUnitCB1t != 0)
               pUnitCB1 = pUnitCB1t;

            if (pUnitCB2t != 0)
               pUnitCB2 = pUnitCB2t;
         }

         /* 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;

   if (pUnitCB1 != 0)
      f_SubmitRequestsToADD(pUnitCB1);

   if (pUnitCB2 != 0)
      f_SubmitRequestsToADD(pUnitCB2);
}


/*------------------------------------------------------------------------
;
;** FT_PutPriorityQueue - Put a request on a priority queue.
;
;   Put a Request Packet or Request List Entry on one of the priority
;   queues in the Unit Control Block for the specified unit.
;
;   VOID PutPriorityQueue  (NPUNITCB pUnitCB, PBYTE pRequest, pUnitCB1,
;                                                            pUnitCB2)
;
;   ENTRY:    pUnitCB          - Pointer to UnitCB
;             pRequest         - Pointer to Request Packet or Request List Entry
;             pUnitCB1         - returned pointer to UnitCB1
;             pUnitCB2         - returned pointer to UnitCB2
;
;
;   RETURN:   VOID
;
;   EFFECTS:
;
------------------------------------------------------------------------*/
VOID FAR f_FT_PutPriorityQueue (pUnitCBin, pRequest, pUnitCB1, pUnitCB2)

NPUNITCB pUnitCBin;
PBYTE    pRequest;
NPUNITCB FAR *pUnitCB1;
NPUNITCB FAR *pUnitCB2;
{
   FT_PutPriorityQueue (pUnitCBin, pRequest, pUnitCB1, pUnitCB2);
}


VOID NEAR FT_PutPriorityQueue (pUnitCBin, pRequest, pUnitCB1, pUnitCB2)

NPUNITCB pUnitCBin;
PBYTE    pRequest;
NPUNITCB FAR *pUnitCB1;
NPUNITCB FAR *pUnitCB2;


{
   FT_RESULTS FT_Results;
   UCHAR      ActionCode, Cmd, LogDriveNum;
   USHORT     SzReqPkt;
   PBYTE      pShadowReq;
   NPVOLCB    pSecVolCB;
   PFTDB      pFTDB;
   NPVOLCB    pVolCB;
   NPUNITCB   pUnitCB;
   PReq_List_Header pRLH;


   *pUnitCB1 = 0;                                                    /*@V99999*/
   *pUnitCB2 = 0;                                                    /*@V99999*/

   if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
   {
      pFTDB = &((PRHFT)pRequest)->ftdb;
      Cmd = ((PPB_Read_Write)pRequest)->RqHdr.Command_Code;
      SzReqPkt = ((((PPB_Read_Write)pRequest)->SG_Desc_Count) << 3) +
                 sizeof(Req_List_Header) + sizeof(PB_Read_Write);
      LogDriveNum = ((PRHFT)pRequest)->Block_Dev_Unit;
   }
   else
   {
      pFTDB = &((PRPFT)pRequest)->ftdb;
      Cmd = ((PRPH)pRequest)->Cmd;
      SzReqPkt = MAXRPSIZE;
      LogDriveNum = ((PRPH)pRequest)->Unit;
   }

   f_Get_VolCB_Addr(LogDriveNum, (NPVOLCB FAR *) &pVolCB);

   pUnitCB = pVolCB->pUnitCB;

   if (f_FT_Request( (pVolCB->FT_PartitionNumber << 8) + Cmd, SzReqPkt,
               (PBYTE FAR *)&FT_Results, (PBYTE FAR *)&ActionCode) != 0)
     /* Handle failure case */
      {                                                              /*@V99999*/
      FT_PutDeferredQueue(pRequest);                                 /*@V99999*/
      return;                                                        /*@V99999*/
      }                                                              /*@V99999*/

   if (ActionCode != ACT_PRIMARY)
       FT_Get_VolCB_Addr( (USHORT)FT_Results.SecPartNum,
                          (NPVOLCB FAR *)&pSecVolCB);

   /* IF ACT_EITHER, do load balancing and put the request on the queue */
   /* with the least number of requests outstanding.                    */

   if (ActionCode == ACT_EITHER)
   {
      if ( (pUnitCB->NumReqsInProgress +
            pUnitCB->NumReqsWaiting)  <=
           (pSecVolCB->pUnitCB->NumReqsInProgress +
            pSecVolCB->pUnitCB->NumReqsWaiting) )

        ActionCode = ACT_PRIMARY_FIRST;

      else

        ActionCode = ACT_SECONDARY_FIRST;
   }

   /* Initialize FTDB area within the original request */

   pFTDB->FT_Flags = FTF_FT_REQUEST;
   pFTDB->OrigRequest.RequestHandle = FT_Results.RequestHandle;


   switch (ActionCode)
   {
      case ACT_PRIMARY:
         f_PutPriorityQueue (pUnitCB, pRequest);
         *pUnitCB1 = pUnitCB;
         break;

      case ACT_PRIMARY_FIRST:
         pFTDB->AltLogDriveNum = pSecVolCB->LogDriveNum;
         f_PutPriorityQueue (pUnitCB, pRequest);
         *pUnitCB1 = pUnitCB;
         break;

      case ACT_SECONDARY:
      case ACT_SECONDARY_FIRST:
         pFTDB->AltLogDriveNum = pVolCB->LogDriveNum;

         if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
            ((PRHFT)pRequest)->Block_Dev_Unit = pSecVolCB->LogDriveNum;
         else
         {
            ((PRP_RWV)pRequest)->rph.Unit = pSecVolCB->LogDriveNum;

            ((PRP_RWV)pRequest)->rba = ((PRP_RWV)pRequest)->rba -
               pVolCB->PartitionOffset - pVolCB->MediaBPB.HiddenSectors +
               pSecVolCB->PartitionOffset + pSecVolCB->MediaBPB.HiddenSectors;
         }

         f_PutPriorityQueue (pSecVolCB->pUnitCB, pRequest);
         *pUnitCB2 = pSecVolCB->pUnitCB;
         break;

      case ACT_BOTH:
         pFTDB->FT_Flags |= FTF_ACT_BOTH;
         pShadowReq = FT_Results.pShadowReq;

         if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
         {
            pRLH = (PReq_List_Header)pRequest;
            OFFSETOF(pRLH) = OFFSETOF(pRequest) -
                     (USHORT) (((PPB_Read_Write)pRequest)->RqHdr.Head_Offset);

            ((PReq_List_Header)pShadowReq)->Count = 1;
            ((PReq_List_Header)pShadowReq)->Request_Control = RLH_Single_Req;
            if (pRLH->Request_Control & RLH_Exe_Req_Seq)
              ((PReq_List_Header)pShadowReq)->Request_Control |= RLH_Exe_Req_Seq;

            ((PReq_List_Header)pShadowReq)->Lst_Status = 0;
            ((PReq_List_Header)pShadowReq)->Block_Dev_Unit =
                                            pSecVolCB->LogDriveNum;
            ((PReq_List_Header)pShadowReq)->y_Done_Count = 0;
            ((PReq_List_Header)pShadowReq)->y_PhysAddr=f_VirtToPhys(pShadowReq);

            OFFSETOF(pShadowReq) = OFFSETOF(pShadowReq)+sizeof(Req_List_Header);
            pFTDB = &((PRHFT)pShadowReq)->ftdb;
            f_BlockCopy(pShadowReq, pRequest, SzReqPkt - sizeof(Req_List_Header));
            ((PPB_Read_Write)pShadowReq)->RqHdr.Length = -1;
            ((PRHFT)pShadowReq)->Block_Dev_Unit = pSecVolCB->LogDriveNum;
            ((PPB_Read_Write)pShadowReq)->RqHdr.Head_Offset
                                          = sizeof(Req_List_Header);
            ((PPB_Read_Write)pShadowReq)->RqHdr.Req_Control = 0;
            ((PPB_Read_Write)pShadowReq)->RqHdr.Status = 0;
         }
         else
         {
            pFTDB = &((PRPFT)pShadowReq)->ftdb;

            f_BlockCopy(pShadowReq, pRequest, MAXRPSIZE);
            ((PRP_RWV)pShadowReq)->rph.Unit = pSecVolCB->LogDriveNum;
            ((PRP_RWV)pShadowReq)->rba = ((PRP_RWV)pRequest)->rba -
                pVolCB->PartitionOffset - pVolCB->MediaBPB.HiddenSectors +
                pSecVolCB->PartitionOffset + pSecVolCB->MediaBPB.HiddenSectors;
         }
         pFTDB->FT_Flags = FTF_FT_REQUEST | FTF_ACT_BOTH | FTF_SHADOW_REQUEST;
         pFTDB->pOrigRequest = pRequest;

         f_PutPriorityQueue (pUnitCB, pRequest);
         f_PutPriorityQueue (pSecVolCB->pUnitCB, pShadowReq);

         *pUnitCB1 = pUnitCB;
         *pUnitCB2 = pSecVolCB->pUnitCB;

   }
}



/*------------------------------------------------------------------------
;
;** FT_NotifyDoneIORB_RLE  - Done processing for fault tolerance requests
;
;   USHORT FT_NotifyDone (NPIORB pIORB, PPB_Read_Write pRequest,
;                                       PPB_Read_Write pOrigRequest)
;
;   ENTRY:    pIORB            - pointer to completed IORB
;             pRequest         - pointer to Request List Entry
;             pOrigRequest     - return pointer to original RLE
;
;   RETURN:   USHORT           - packet status word
;
;   EFFECTS:
;
------------------------------------------------------------------------*/
USHORT FAR f_FT_NotifyDoneIORB_RLE (pIORB, pRequest, pOrigRequest)

NPIORB            pIORB;
PPB_Read_Write    pRequest;
PPB_Read_Write    FAR *pOrigRequest;
{
   return(FT_NotifyDoneIORB_RLE (pIORB, pRequest, pOrigRequest));
}

USHORT NEAR FT_NotifyDoneIORB_RLE (pIORB, pRequest, pOrigRequest)

NPIORB            pIORB;
PPB_Read_Write    pRequest;
PPB_Read_Write    FAR *pOrigRequest;
{
   USHORT  DoneParm1;
   USHORT  RequestHandle;
   USHORT  RC;
   NPVOLCB pVolCB;
   PFTDB   pFTDB;
   PBYTE   pReq;                                                     /*@V99999*/

   pFTDB = &((PRHFT)pRequest)->ftdb;
   f_Get_VolCB_Addr ( ((PRHFT)pRequest)->Block_Dev_Unit, (NPVOLCB FAR*)&pVolCB);
   DoneParm1 = pVolCB->FT_PartitionNumber << 8;

   if ( (pRequest->RqHdr.Status & 0xF0) == RH_NO_ERROR)
      (UCHAR) DoneParm1 = 0xFF;
   else
      (UCHAR) DoneParm1 = pRequest->RqHdr.Error_Code;

   if (pFTDB->FT_Flags & FTF_SHADOW_REQUEST)
   {
      (PBYTE) *pOrigRequest = pFTDB->pOrigRequest;
      RequestHandle = ((PRHFT)(*pOrigRequest))->ftdb.OrigRequest.RequestHandle;
   }
   else
   {
      *pOrigRequest = pRequest;
      RequestHandle = pFTDB->OrigRequest.RequestHandle;
   }

   RC = f_FT_Done(DoneParm1, RequestHandle, pRequest->Start_Block);


   if  (RC & STDON)
   {
      pRequest = *pOrigRequest;

      if (RC & STERR)
      {
         pRequest->RqHdr.Status = RH_UNREC_ERROR;
         pRequest->Blocks_Xferred = 0;

         if ( (UCHAR)RC >= I24_MIN_RECOV_ERROR )
            pRequest->RqHdr.Status = RH_RECOV_ERROR;
         pRequest->RqHdr.Error_Code = (UCHAR) RC;
      }
      else
      {
         pRequest->RqHdr.Status = RH_NO_ERROR;
         pRequest->Blocks_Xferred = pRequest->Block_Count;
         pRequest->RqHdr.Error_Code = 0;
      }

      /*  Check to see if any requests were deferred  */             /*@V99999*/
      if (NumReqsDeferred != 0)                                      /*@V99999*/
      {                                                              /*@V99999*/
         if (FT_PullDeferredQueue( (PBYTE FAR *)&pReq) == NO_ERROR)  /*@V99999*/
            FT_PutDeferredRequest(pReq);                             /*@V99999*/
      }                                                              /*@V99999*/
      return(STDON);
   }

  /* Request is not done.  For mirrored writes, no further work is  */
  /* needed. Just wait for the second write to complete.  For reads,*/
  /* re-queue the request to the alternate unit.                    */

  if ( !(pFTDB->FT_Flags & FTF_ACT_BOTH) )
  {
     f_Get_VolCB_Addr(pFTDB->AltLogDriveNum, (NPVOLCB FAR *)&pVolCB);
     ((PRHFT)pRequest)->Block_Dev_Unit = (UCHAR) pVolCB->LogDriveNum;
     pRequest->RqHdr.Status = 0;
     f_PutPriorityQueue (pVolCB->pUnitCB, (PBYTE) pRequest);
     f_SubmitRequestsToADD(pVolCB->pUnitCB);
  }
  return(0);
}


/*------------------------------------------------------------------------
;
;** FT_NotifyDoneIORB_RP  - Done processing for fault tolerance requests
;
;   USHORT FT_NotifyDoneIORB_RP (NPIORB pIORB,  PBYTE FAR *pRequest,
;                                               PBYTE FAR *pOrigRequest)
;
;   ENTRY:    pIORB            - pointer to completed IORB
;             pRequest         - pointer to Request Packet
;             pOrigRequest     - returned pointer to orig shadow RP
;
;   RETURN:   USHORT           - packet status word
;
;   EFFECTS:
;
------------------------------------------------------------------------*/
USHORT FAR f_FT_NotifyDoneIORB_RP  (pIORB, pRequest, pOrigRequest)

NPIORB    pIORB;
PRP_RWV   pRequest;
PRP_RWV   FAR *pOrigRequest;
{
   return(FT_NotifyDoneIORB_RP  (pIORB, pRequest, pOrigRequest));
}

USHORT NEAR FT_NotifyDoneIORB_RP  (pIORB, pRequest, pOrigRequest)

NPIORB    pIORB;
PRP_RWV   pRequest;
PRP_RWV   FAR *pOrigRequest;

{
   USHORT  DoneParm1;
   USHORT  RequestHandle;
   ULONG   StartBlock;
   USHORT  RC;
   NPVOLCB pVolCB;
   PFTDB   pFTDB;
   PBYTE   pReq;                                                     /*@V99999*/

   pFTDB = &((PRPFT)pRequest)->ftdb;
   f_Get_VolCB_Addr(pRequest->rph.Unit, (NPVOLCB FAR *)&pVolCB);
   DoneParm1 = pVolCB->FT_PartitionNumber << 8;

   StartBlock = pRequest->rba - pVolCB->PartitionOffset -
                                pVolCB->MediaBPB.HiddenSectors;

   if (pRequest->rph.Status & STERR)
      (UCHAR) DoneParm1 = (UCHAR) pRequest->rph.Status;
   else
      (UCHAR) DoneParm1 = 0xff;

   if (pFTDB->FT_Flags & FTF_SHADOW_REQUEST)
   {
       (PBYTE) *pOrigRequest = pFTDB->pOrigRequest;
       RequestHandle = ((PRPFT)(*pOrigRequest))->ftdb.OrigRequest.RequestHandle;
   }
   else
   {
      *pOrigRequest = pRequest;
      RequestHandle = pFTDB->OrigRequest.RequestHandle;
   }

   RC = f_FT_Done(DoneParm1, RequestHandle, StartBlock);

   if  (RC & STDON)
   {
      pRequest = *pOrigRequest;
      pRequest->rph.Status = RC;

      if (RC & STERR)
        pRequest->NumSectors = 0;

      /*  Check to see if any requests were deferred  */             /*@V99999*/
      if (NumReqsDeferred != 0)                                      /*@V99999*/
      {                                                              /*@V99999*/
         if (FT_PullDeferredQueue( (PBYTE FAR *)&pReq) == NO_ERROR)  /*@V99999*/
            FT_PutDeferredRequest(pReq);                             /*@V99999*/
      }                                                              /*@V99999*/
      return(STDON);
   }

  /* Request is not done.  For mirrored writes, no further work is  */
  /* needed. Just wait for the second write to complete.  For reads,*/
  /* re-queue the request to the alternate unit.                    */

  if ( !(pFTDB->FT_Flags & FTF_ACT_BOTH) )
  {
     f_Get_VolCB_Addr(pFTDB->AltLogDriveNum, (NPVOLCB FAR *)&pVolCB);
     pRequest->rph.Unit = pVolCB->LogDriveNum;
     pRequest->rba = StartBlock + pVolCB->PartitionOffset +
                                  pVolCB->MediaBPB.HiddenSectors;
     f_PutPriorityQueue (pVolCB->pUnitCB, (PBYTE) pRequest);
     f_SubmitRequestsToADD(pVolCB->pUnitCB);
  }
  return(0);
}
/*------------------------------------------------------------------------
;
;** FT_CheckFTSupport  - See if request requires fault tolerance support
;
;   Check to see if the request requires fault tolerance support
;
;   USHORT    FT_CheckFTSupport (NPUNITCB pUnitCB, PBYTE pRequest)
;
;   ENTRY     pUnitCB     - Pointer to UnitCB
;             pRequest    - Pointer to Request Packet or Request List Entry
;
;   RETURN:   USHORT      - YES = FT required,
;                           NO = No FT required
;   EFFECTS:
;
------------------------------------------------------------------------*/
USHORT FAR f_FT_CheckFTSupport (pUnitCB, pRequest)

NPUNITCB   pUnitCB;
PBYTE      pRequest;
{
   return(FT_CheckFTSupport (pUnitCB, pRequest));
}

USHORT NEAR FT_CheckFTSupport (pUnitCB, pRequest)

NPUNITCB   pUnitCB;
PBYTE      pRequest;

{

   static UCHAR FTCmdTable[] =
   {
      CMDINPUT, CMDOUTPUT, CMDOUTPUTV,
      CMDInputBypass, CMDOutputBypass, CMDOutputBypassV,
      PB_READ_X, PB_WRITE_X, PB_WRITEV_X
   };

   UCHAR  Cmd;
   USHORT i;
   PReq_List_Header pRLH;

   /* See if FT is enabled */

   if ( !(DDFlags & DDF_FT_ENABLED) )
      return(NO);

   /* No FT support for removable media                           */

   if (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
      return(NO);

   /* No FT support for I/O to physical drives, i.e. 0x80, etc)   */

   if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
   {
      SELECTOROF(pRLH) = SELECTOROF(pRequest);
      OFFSETOF(pRLH) = OFFSETOF(pRequest) -
              (USHORT) ((PPB_Read_Write)pRequest)->RqHdr.Head_Offset;

      if (pRLH->Block_Dev_Unit & 0x80)
         return(NO);

      Cmd = ((PPB_Read_Write)pRequest)->RqHdr.Command_Code;
   }
   else
   {
      if ( ((PRPH)pRequest)->Unit & 0x80 )
        return(NO);

      Cmd = ((PRPH)pRequest)->Cmd;
   }

   /* See if it's a command which requires FT support */

   for (i = 0; i < sizeof(FTCmdTable); i++)
   {
      if (Cmd == FTCmdTable[i])
         return(YES);
   }

   return(NO);
}


/*------------------------------------------------------------------------
;
;** FT_Get_VolCB_Addr - Return a pointer to a VolCB for the specified Drive
;
;   USHORT NEAR FT_Get_VolCB_Addr   (USHORT PartNum, NPVOLCB *pVolCB)
;
;   ENTRY:    ParmNum          - FT Partition Number
;             pVolCB           - returned pointer to VolCB
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if VolCB found)
;
;   EFFECTS:
;
;   NOTES:
------------------------------------------------------------------------*/
USHORT FAR f_FT_Get_VolCB_Addr (PartNum, pVolCB)

USHORT  PartNum;
NPVOLCB FAR *pVolCB;
{
   return(FT_Get_VolCB_Addr (PartNum, pVolCB));
}

USHORT FT_Get_VolCB_Addr (PartNum, pVolCB)

USHORT  PartNum;
NPVOLCB FAR *pVolCB;
{
   USHORT  found = FALSE;
   NPVOLCB pVolCBx;

   if ( !(DDFlags & DDF_NO_MEDIA) )
   {
      pVolCBx = VolCB_Head;

      while (pVolCBx != NULL && found == FALSE)
      {
         if (pVolCBx->FT_PartitionNumber == PartNum)
            found = TRUE;
         else
            pVolCBx = pVolCBx->pNextVolCB;
      }
   }

   if (found == TRUE)
   {
      *pVolCB = pVolCBx;
      return(NO_ERROR);
   }
   else
      return(ERROR);
}


/*------------------------------------------------------------------------
;
;** FT_LockFTCode - Lock FT Code in swap segment
;
;   USHORT NEAR FT_LockFTCode (VOID)
;
;   ENTRY:    VOID
;
;   RETURN:   USHORT           - Packet Status Word
;
;   EFFECTS:
;
;   NOTES:
------------------------------------------------------------------------*/
USHORT FT_LockFTCode()

{
   ULONG plFTCode;             /* Linear address of Parm or Data Packet */
   ULONG plLockHandle;
   UCHAR LockHandle[12];
   ULONG PageListCount;
   USHORT Length;
   USHORT rc;

   (PVOID) plFTCode = (PVOID) f_FT_ExecReqList;
   rc = DevHelp_VirtToLin(SELECTOROF(plFTCode),
                            (ULONG) OFFSETOF(plFTCode),
                            (PVOID) &plFTCode);

   (PVOID) plLockHandle = (PVOID) &LockHandle;
   rc = DevHelp_VirtToLin(SELECTOROF(plLockHandle),
                            (ULONG) OFFSETOF(plLockHandle),
                            (PVOID) &plLockHandle);

   Length = (USHORT) FT_CodeSegEnd - (USHORT) f_FT_ExecReqList;

   if (DevHelp_VMLock(VMDHL_LONG, plFTCode, Length, -1,
                  plLockHandle, (PULONG) &PageListCount) != 0)

      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);


   return(STDON);

}

/*  lpq   */
/*------------------------------------------------------------------------
;
;** FT_PutDeferredQueue  - Put a Request on a deferred queue.
;
;   This routine places an RLE or RP on the apropriate deferred queue when
;   the call to FT_Request fails (due to lack of resources).
;
;   VOID FT_PutDeferredQueue  (PBYTE pRequest)
;
;   ENTRY:    pRequest         - Pointer to Request List Entry or Request Pkt
;
;   RETURN:   VOID
;
;   EFFECTS:
;
------------------------------------------------------------------------*/
VOID FT_PutDeferredQueue  (pRequest)

PBYTE  pRequest;

{

   PUSHFLAGS;
   DISABLE;

   /* Place on Request List Entry Deferred Queue  */

   if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
   {
      if (DeferredQRLE.Head == 0)
      {
         DeferredQRLE.Head = pRequest;
         DeferredQRLE.Tail = pRequest;
         ((PPB_Read_Write)pRequest)->RqHdr.Waiting = 0;
      }
      else
      {
         ((PPB_Read_Write)(DeferredQRLE.Tail))->RqHdr.Waiting = (ULONG)pRequest;
         DeferredQRLE.Tail =  pRequest;
      }
   }
   else
   /* Place on Request Packet Deferred Queue  */
   {
      if (DeferredQRP.Head == 0)
      {
         DeferredQRP.Head = pRequest;
         DeferredQRP.Tail = pRequest;
         ((PRPH)pRequest)->Link = 0;
      }
      else
      {
         ((PRPH)(DeferredQRP.Tail))->Link = (PRPH) pRequest;
         DeferredQRP.Tail =  pRequest;
      }
   }

   NumReqsDeferred++;

   ENABLE;
   POPFLAGS;

}


/*------------------------------------------------------------------------
;
;** FT_PullDeferredQueue - Pull a request from a deferred queue.
;
;   This routine removes a Request List Entry, or a Request Packet from
;   the deferred queues.
;
;   USHORT    FT_PullDeferredQueue (PBYTE *pReqAddr)
;
;   ENTRY:    pReqAddr     - Returned pointer to pulled Request List
;                            Entry or Request Packet
;
;   RETURN:   USHORT       -  =  0,  Request pulled
;                             <> 0,  No Request pulled, queues empty
;   EFFECTS:
;
------------------------------------------------------------------------*/
USHORT  FT_PullDeferredQueue (pReqAddr)

   PBYTE  FAR *pReqAddr;
{

   PBYTE    pRequest;

   PUSHFLAGS;
   DISABLE;


   /* Search the Request List Deferred Queue First  */

   if (DeferredQRLE.Head != 0)
   {
      pRequest = DeferredQRLE.Head;
      DeferredQRLE.Head = (PBYTE) ((PPB_Read_Write)pRequest)->RqHdr.Waiting;
      ((PPB_Read_Write)pRequest)->RqHdr.Waiting = 0;
      if (DeferredQRLE.Head == 0)
         DeferredQRLE.Tail = 0;
   }

   /* If it was empty, search the Request Packet Queue   */

   else if (DeferredQRP.Head != 0)
   {
      pRequest = DeferredQRP.Head;
      DeferredQRP.Head = (PBYTE) (((PRPH)pRequest)->Link);
      ((PRPH)pRequest)->Link = 0;
      if (DeferredQRP.Head == 0)
         DeferredQRP.Tail = 0;
   }
   else
      return(ERROR);

   *pReqAddr = pRequest;
   NumReqsDeferred--;

   ENABLE;
   POPFLAGS;
   return(NO_ERROR);
}

/*------------------------------------------------------------------------
;
;** FT_PutDeferredRequest   - Put a deferred request on a priority queue
;
;   This routine takes a request that was pulled from the deferred
;   queue and attempts to queue it on a priority queue by calling
;   FT_PutPriorityqueue.
;
;   VOID   FT_PutDeferredRequest (pRequest);
;
;   ENTRY:    pRequest     - Pointer to Request List Entry or Request Pkt
;
;   RETURN:   VOID
;
------------------------------------------------------------------------*/

VOID   FT_PutDeferredRequest (pRequest)

PBYTE   pRequest;

{
   UCHAR      LogDriveNum;
   NPVOLCB    pVolCB;
   NPUNITCB   pUnitCB1  = 0;
   NPUNITCB   pUnitCB2  = 0;


   /*  Get the Unit number and retrieve the Volume Control Block  */
   if ( ((PRPH)pRequest)->Cmd == PB_REQ_LIST )
      LogDriveNum = ((PRHFT)pRequest)->Block_Dev_Unit;
   else
      LogDriveNum = ((PRPH)pRequest)->Unit;

   f_Get_VolCB_Addr(LogDriveNum, (NPVOLCB FAR *) &pVolCB);


   FT_PutPriorityQueue(pVolCB->pUnitCB,  pRequest, (NPUNITCB FAR *) &pUnitCB1,
                                                   (NPUNITCB FAR *) &pUnitCB2);

   if (pUnitCB1 != 0)
      f_SubmitRequestsToADD(pUnitCB1);

   if (pUnitCB2 != 0)
      f_SubmitRequestsToADD(pUnitCB2);
}

VOID FT_CodeSegEnd()
{
}
