/*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/os2aspi/aspisrb.c, aspi, r206 93/03/20";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = ASPISRB.C
 *
 * DESCRIPTIVE NAME = OS2ASPI.DMD - OS/2 ASPI Device Manager
 *                    ASPI SRB Processor
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  Converts ASPI SRBs to corresponding IORB(s).
 *
 *
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include <os2.h>
#include <strat2.h>

#include <devcmd.h>
#include <devclass.h>

#include <reqpkt.h>
#include <iorb.h>
#include <dhcalls.h>
#include <dskinit.h>
#include <scsi.h>

#include <aspi.h>
#include <aspicons.h>
#include <aspitype.h>
#include <aspipro.h>
#include <aspiextn.h>

extern      PVOID                pDataSeg;
extern      ULONG                ppDataSeg;
extern      USHORT               IORBWaitSem;
extern      UCHAR                numberOfASPIAdapters;
extern      CHAR                 OS2ASPI_Text[];
extern      NPACB                npACBAnchor;
extern      NPATE                npFirstATE;
extern      NPSRB_LINK           npSRBLinkFreeList;
extern      NPIORB_UNIT_CONTROL  npIORBUnitControl;
extern      USHORT               allocationOverride;
extern      USHORT               shareTargets;

/*********************************************************
*                                                        *
*   Procedure Name : ASPISRBEntr                         *
*                                                        *
*   Description : This procedure routes all ASPI         *
*   requests to the appropriate function.                *
*                                                        *
*   Input :                                              *
*         pSRBH - A pointer to SRB header                *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR _loadds ASPISRBEntr(PASPI_SRB_HEADER pSRBH,USHORT virtualASPI)

   {
   switch (pSRBH->CommandCode)
      {
      case ASPI_CMD_ADAPTER_INQUIRY:
         DoASPIInquiry((PASPI_SRB_INQUIRY) pSRBH,virtualASPI);
         break;

      case ASPI_CMD_GET_DEVICE_TYPE:
         DoASPIDeviceType((PASPI_SRB_DEVICE_TYPE) pSRBH, virtualASPI);
         break;

      case ASPI_CMD_EXECUTE_IO:
         DoASPIExecuteIO((PASPI_SRB_EXECUTE_IO) pSRBH,virtualASPI);
         break;

      case ASPI_CMD_ABORT_IO:
         DoASPIAbortIO((PASPI_SRB_ABORT_IO) pSRBH,virtualASPI);
         break;

      case ASPI_CMD_RESET_DEVICE:
         DoASPIExecuteIO((PASPI_SRB_EXECUTE_IO) pSRBH,virtualASPI);
         break;

      case ASPI_CMD_SET_ADAPTER_PARMS:
         DoASPISetAdapterParms((PASPI_SRB_ADAPTER_PARMS) pSRBH,virtualASPI);
         break;

      default:
         pSRBH->ASPIStatus = ASPI_STATUS_INVALID_COMMAND;
       }
   }


/*********************************************************
*                                                        *
*   Procedure Name : DoASPIInquiry                       *
*                                                        *
*   Description : This procedure handles host adapter    *
*   inquires.                                            *
*                                                        *
*   Input :                                              *
*         pSRBI - A pointer to the SRB for host adapter  *
*            inquiries                                   *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :  This function returns the status for the   *
*   request packet that was just handled by it.          *
*                                                        *
*********************************************************/
USHORT NEAR DoASPIInquiry(PASPI_SRB_INQUIRY pSRBI, USHORT virtualASPI)

   {
   UCHAR    adapterIndex;
   NPACB    pACB;
   UCHAR    ASPIStatus;
   USHORT   extendedByteCount;                                       /*@V53040*/
   USHORT   requestPacketStatus = STDON;

   /* Verify that host adapter is valid */
   adapterIndex = pSRBI->SRBHdr.AdapterIndex;

   if (adapterIndex >= numberOfASPIAdapters)
      {
      ASPIStatus = ASPI_STATUS_INVALID_ADAPTER;
      requestPacketStatus |= STERR;
      }
   else
      {
      pACB = FindAdapter(adapterIndex);

      /* Copy all of the information of the current host adapter */
      /* into the SRB                                            */
      memcpy(pSRBI->ManagerName,
             OS2ASPI_Text,
             sizeof(pSRBI->ManagerName));
      memset(pSRBI->AdapterParms,
             0,
             sizeof(pSRBI->AdapterParms));

      pSRBI->AdapterCount = numberOfASPIAdapters;
      pSRBI->AdapterTargetID = pACB->AdapterTargetID;
      memcpy(pSRBI->AdapterName,
             pACB->AdapterName,
             sizeof(pSRBI->ManagerName));

      /* Determine if we should return EXTENDED adapter information */
      if (*((PUSHORT) &pSRBI->Reserved_1) == 0xAA55)
         {                                                           /*@V53040*/
         /* Determine how many extended bytes should be returned */  /*@V53040*/
         extendedByteCount = *(((PUSHORT) &(pSRBI->Reserved_1)) + 1);/*@V53040*/
         if (extendedByteCount > MAX_EXTENDED_ADAPTER_COUNT)
            extendedByteCount = MAX_EXTENDED_ADAPTER_COUNT;
                                                                     /*@V53040*/
         memcpy((PBYTE) &(pSRBI->AdapterFeatures),
                (PBYTE) &(pACB->AdapterFeatures),
                extendedByteCount);
                                                                     /*@V53040*/
         /* Update number of bytes transferred and signature */
         *(((PUSHORT) &(pSRBI->Reserved_1)) + 1) = extendedByteCount;/*@V53040*/
         *((PUSHORT) &pSRBI->Reserved_1) == 0x55AA;
         }                                                           /*@V53040*/

      ASPIStatus = ASPI_STATUS_NO_ERROR;
      }

   pSRBI->SRBHdr.ASPIStatus = ASPIStatus;

   if ((virtualASPI & VDM_REQUEST) != 0)
      CallVirtPostRoutine((PVOID) pSRBI);

   return(requestPacketStatus);
   }

/*********************************************************
*                                                        *
*   Procedure Name : DoASPIDeviceType                    *
*                                                        *
*   Description : This procedure handles ASPI inquiries  *
*   about a device.                                      *
*                                                        *
*   Input :                                              *
*         pSRBDT - A pointer to the SRB for device       *
*            inquiries                                   *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :   This function returns the status for the  *
*   request packet that was just handled by it.          *
*                                                        *
*********************************************************/
USHORT NEAR DoASPIDeviceType(PASPI_SRB_DEVICE_TYPE pSRBDT, USHORT virtualASPI)

   {
   UCHAR    adapterIndex;
   UCHAR    ASPIStatus;
   NPATE    npATE;
   USHORT   requestPacketStatus = STDON;
   USHORT   allocationResult;                                        /*@V64399*/
   USHORT   allocationStatus;                                        /*@V64399*/
   USHORT   extendedByteCount;
                                                                     /*@V64399*/
   /* Assume the target is accessible */                             /*@V64399*/
   allocationResult = 0;                                             /*@V64399*/

   /* Verify that the host adapter is valid */
   adapterIndex = pSRBDT->SRBHdr.AdapterIndex;

   if (adapterIndex >= numberOfASPIAdapters)
      {
      ASPIStatus = ASPI_STATUS_INVALID_ADAPTER;
      requestPacketStatus |= STERR;
      }
   else
      {
      /* Verify that the target device is attached to the host adapter */
      npATE = FindTarget(adapterIndex,
                         pSRBDT->DeviceTargetID,
                         pSRBDT->DeviceTargetLUN);

      if (npATE)
         {
         /* Remember if the target is ALREADY allocated */
         allocationStatus = npATE->Flags & (ATEF_ASPI_ALLOCATED |
                                            ATEF_IGNORE_ALLOCATION);

         /* If the target is not already allocated, allocate it */   /*@V64399*/
         if (!allocationStatus)
            allocationResult = SendUnitControlCommand(npATE,         /*@V64399*/
                                              IOCM_ALLOCATE_UNIT);   /*@V64399*/
                                                                     /*@V64399*/
         /* If allocation failed, it was most like allocated AFTER */
         /* the ASPI manager was initialized. If allocation checking */
         /* is being ignored, update the allocation status and continue */
         if ((allocationOverride) &&
             (allocationResult))
            {
            npATE->Flags |= ATEF_IGNORE_ALLOCATION;
            npATE->TargetFeatures |= ASPI_DEVICE_ALLOCATION_SHARED;
            allocationResult = 0;
            }

         /* If target has been allocated to us, return device type *//*@V64399*/
         if (!allocationResult)                                      /*@V64399*/
            {                                                        /*@V64399*/
            /* The device is available, so return the type reported */
            pSRBDT->DeviceType = npATE->DeviceType;

            /* If the device was not already allocated to us,     */ /*@V64399*/
            /* release it so that we can return information about */ /*@V64399*/
            /* all available targets. The target will be          */ /*@V64399*/
            /* permanently allocated when the first command is    */ /*@V64399*/
            /* passed down to it.                                 */ /*@V64399*/

            /* If allocation failed earlier and we are now sharing it */
            /* with another manager there is no deallocation needed. */
            if ((!allocationStatus) &&
                (!(npATE->Flags & ATEF_IGNORE_ALLOCATION)))
               SendUnitControlCommand(npATE,IOCM_DEALLOCATE_UNIT);

            /* Determine if we should return EXTENDED target information */
            if (*((PUSHORT) &pSRBDT->Reserved_1) == 0xAA55)
               {
               /* Determine how many extended bytes should be returned */
               extendedByteCount = *(((PUSHORT) &(pSRBDT->Reserved_1)) + 1);
               if (extendedByteCount > MAX_EXTENDED_TARGET_COUNT)
                  extendedByteCount = MAX_EXTENDED_TARGET_COUNT;

               memcpy((PBYTE) &(pSRBDT->TargetFeatures),
                      (PBYTE) &(npATE->TargetFeatures),
                      extendedByteCount);

               /* Update number of bytes transferred and signature */
               *(((PUSHORT) &(pSRBDT->Reserved_1)) + 1) = extendedByteCount;
               *((PUSHORT) &pSRBDT->Reserved_1) == 0x55AA;
               }

            ASPIStatus = ASPI_STATUS_NO_ERROR;
            }
         else
            {
            ASPIStatus = ASPI_STATUS_INVALID_TARGET;
            requestPacketStatus |= STERR;
            }
         }                                                           /*@V64399*/
      else
         {
         ASPIStatus = ASPI_STATUS_INVALID_TARGET;
         requestPacketStatus |= STERR;
         }
      }

   pSRBDT->SRBHdr.ASPIStatus = ASPIStatus;

   if ((virtualASPI & VDM_REQUEST) != 0)
      CallVirtPostRoutine((PVOID) pSRBDT);

   return(requestPacketStatus);
   }


/*********************************************************
*                                                        *
*   Procedure Name : DoASPIExecuteIO                     *
*                                                        *
*   Description : This procedure handles requests to     *
*   send commands directly to a device.                  *
*                                                        *
*   Input :                                              *
*         pSRBIO - A pointer to the SRB for I/O          *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR DoASPIExecuteIO(PASPI_SRB_EXECUTE_IO pSRBIO, USHORT virtualASPI)

   {
   UCHAR   adapterIndex;
   UCHAR   ASPIStatus;
   UCHAR   ASPICmd;
   NPATE   npATE;
   USHORT  allocationResult = 0;

   adapterIndex = pSRBIO->SRBHdr.AdapterIndex;
   ASPICmd      = pSRBIO->SRBHdr.CommandCode;

   /* Verify that the host adapter is valid */
   if (adapterIndex >= numberOfASPIAdapters)
      ASPIStatus = ASPI_STATUS_INVALID_ADAPTER;
   else
      {
      /* Verify that the target device is attached to the host adapter */
      npATE = FindTarget(adapterIndex,
                         pSRBIO->DeviceTargetID,
                         pSRBIO->DeviceTargetLUN);

      if (npATE)
         {
         /* If the device was allocated by OS2ASPI or if access is being */
         /* shared with another manager WITHOUT deallocation, send the SRB. */
         /* Otherwise, allocate the target and then send the SRB */
         if (!((npATE->Flags & ATEF_ASPI_ALLOCATED) ||
               (npATE->Flags & ATEF_IGNORE_ALLOCATION)))
             allocationResult = SendUnitControlCommand(npATE,
                                                       IOCM_ALLOCATE_UNIT);

         /* If allocation failed, it was most like allocated AFTER */
         /* the ASPI manager was initialized. If allocation checking */
         /* is being ignored, update the allocation status and continue */
         if ((allocationOverride) &&
             (allocationResult))
            {
            npATE->Flags |= ATEF_IGNORE_ALLOCATION;
            npATE->TargetFeatures |= ASPI_DEVICE_ALLOCATION_SHARED;
            allocationResult = 0;
            }

         if (!allocationResult)
            {
            /* The unit is available, so mark the SRB in progress */
            /* and issue the command to the target                */
            ASPIStatus = ASPI_STATUS_IN_PROGRESS;
            pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_IN_PROGRESS;

            switch (ASPICmd)
               {
               case ASPI_CMD_EXECUTE_IO:
                  SRBToIORBPassThru(npATE,                           /*@V61092*/
                                    pSRBIO,
                                    0,
                                    virtualASPI);
                  break;
               case ASPI_CMD_RESET_DEVICE :
                  SRBToIORBReset(npATE,                              /*@V61092*/
                                 (PASPI_SRB_RESET_DEVICE) pSRBIO,
                                 0,
                                 virtualASPI);
                  break;
               default :
                  ASPIStatus = ASPI_STATUS_INVALID_COMMAND;
               }
            }
         else
            {
            /* The selected target is already in use */
            ASPIStatus = ASPI_STATUS_ERROR;
            pSRBIO->HostStatus = ASPI_HSTATUS_SELECTION_TIMEOUT;
            }
         }
      else
         ASPIStatus = ASPI_STATUS_INVALID_TARGET;
      }

   /* If the request is not pending, report the error.          */
   /* All successful completions are reported in NotifyIORBDone */
   if (ASPIStatus != ASPI_STATUS_IN_PROGRESS)
      {
      pSRBIO->SRBHdr.ASPIStatus = ASPIStatus;

      /* Call the ASPI posting routine (if necessary) */
      if ((virtualASPI & VDM_REQUEST) != 0)
          CallVirtPostRoutine((PVOID)pSRBIO);
      else
         if (pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_POST_ENABLE)
            CallPostRoutine(pSRBIO);
      }
   }

#pragma optimize("cegl",off)        /* Disable optimization switches that    */
                                    /* are illegal for routines that contain */
                                    /* inline assembly code                  */
/*********************************************************
*                                                        *
*   Procedure Name : DoASPIAbortIO                       *
*                                                        *
*   Description : This procedure handles requests to     *
*   abort commands being executed by a device.           *
*                                                        *
*   Input :                                              *
*         pSRBIO - A pointer to the SRB for I/O          *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR DoASPIAbortIO(PASPI_SRB_ABORT_IO pSRBIO, USHORT virtualASPI)

   {
   UCHAR    adapterIndex;
   NPATE    npATE;
   USHORT   resultCode;
   USHORT   modeFlag;
   PASPI_SRB_EXECUTE_IO pAbortSRB;
   PASPI_SRBWORK pASPIWork;

   /* Verify that the host adapter is valid */
   adapterIndex = pSRBIO->SRBHdr.AdapterIndex;

   if (adapterIndex >= numberOfASPIAdapters)
      pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_INVALID_ADAPTER;
   else
      {
      /* Convert physical address of SRB being aborted to virtual address */
      /* and release it after extracting the targetID and targetLUN       */
      resultCode = DevHelp_PhysToVirt(pSRBIO->ppSRB,
                                      (USHORT) sizeof(ASPI_SRB_EXECUTE_IO),
                                      (PPVOID)&pAbortSRB,
                                      &modeFlag);

      /* The driver must not yield while the virtual address is being used */
      DISABLE
      npATE = FindTarget(adapterIndex,
                         pAbortSRB->DeviceTargetID,
                         pAbortSRB->DeviceTargetLUN);
      if (npATE)
         {
         /* Retrieve the virtual pointer used by the VDD */
         /* in case this request is part of a VDM_DESTROY event. */
         pASPIWork = (PASPI_SRBWORK) pAbortSRB->ASPIWorkSpace;
         }
      ENABLE

      DevHelp_UnPhysToVirt(&modeFlag);

      if (npATE)
         {
         /* Abort IORB pending on the device for specified SRB.       */
         /* Queues will automatically be cleaned up by NotifyIORBDone */
         /* as each SRB is aborted.                                   */
         AbortIORB(npATE,
                   pSRBIO,
                   pASPIWork,
                   0,
                   virtualASPI);

         }
      else
         pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_INVALID_TARGET;
      }
   }

#pragma optimize("cegl",on)         /* Enable optimization switches again */

/*********************************************************
*                                                        *
*   Procedure Name : AbortIORB                           *
*                                                        *
*   Description : This procedure attempts to abort the   *
*   IORB that was created for a specified SRB. Since     *
*   this IORB has already been passed to the ADD we      *
*   cannot assume it will succeed. The actual success    *
*   or failure of this command must be verified in the   *
*   status of the ABORT SRB.                             *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the command target's ATE  *
*         pSRBIO - A pointer to the SRB                  *
*         pSRBToAbortWork - A pointer to the work space  *
*           of the SRB being aborted.                    *
*         npSRBLink - A pointer to the SRB_LINK that is  *
*           allocated for the current command.           *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/           /*@V61092*/
VOID NEAR AbortIORB(NPATE npATE,PASPI_SRB_ABORT_IO pSRBIO,
                    PASPI_SRBWORK pSRBToAbortWork, NPSRB_LINK npSRBLink,
                    USHORT virtualASPI)

   {
   PIORB_DEVICE_CONTROL pIORB;
   PIORB_DMWORK pDMWork;
   ULONG physSRBIO;
   ULONG SRBLockHandle;
   PIORBH pIORBToAbort;

   /* Allocate an IORB for the current SRB if one is not already available */
   if (!npSRBLink)                                                        /*@V61092*/
      npSRBLink = AllocateIORB(npATE,
                               (PASPI_SRB_HEADER) pSRBIO,
                               AbortIORB,
                               virtualASPI);/*@V61092*/
                                                                          /*@V61092*/
   if (npSRBLink)                                                         /*@V61092*/
      {
      /* Create an IORB that the ADD can handle */
      pIORB = (PIORB_DEVICE_CONTROL) &npSRBLink->IORB;
      pDMWork = (PIORB_DMWORK) &(pIORB->iorbh.DMWorkSpace);

      memset((PBYTE) pIORB,0,sizeof(IORB_DEVICE_CONTROL));
      pIORB->iorbh.Length = sizeof(IORB_DEVICE_CONTROL);
      pIORB->iorbh.UnitHandle = npATE->UnitHandle;
      pIORB->iorbh.CommandCode = IOCC_DEVICE_CONTROL;
      pIORB->iorbh.CommandModifier = IOCM_ABORT;

      pIORB->iorbh.NotifyAddress = &NotifyIORBDone;
      pIORB->iorbh.StatusBlockLen = sizeof(SCSI_STATUS_BLOCK);
      pIORB->iorbh.pStatusBlock = (NPBYTE) &npSRBLink->commandStatus;

      /* Clear the status block before using it */
      memset((PBYTE) &npSRBLink->commandStatus,0,sizeof(SCSI_STATUS_BLOCK));

      /* Save DS and pointers to the request packet and current ATE */
      pDMWork->npATE = npATE;
      pDMWork->virtualASPI = virtualASPI;
      npSRBLink->OrigSRBPtr  = (PVOID) pSRBIO;


      /* Lock the SRB selector in place and record its location. */
      /* This really should have been done by the caller, but it is */
      /* nice to try and be sure. */
      DevHelp_Lock(SELECTOROF(pSRBIO),
                   1,
                   1,
                   &SRBLockHandle);

      DevHelp_VirtToPhys(pSRBIO,
                         &physSRBIO);

      /* Copy the SRB selector to the GDT so we can use it later */
      pDMWork->SRBGDTSelector = npSRBLink->SRBGDTSelector;           /*@V58231*/
      DevHelp_PhysToGDTSelector(physSRBIO,
                                sizeof(ASPI_SRB_ABORT_IO),
                                pDMWork->SRBGDTSelector);

      /* Release our lock on the SRB because it cannot be done */
      /* at interrupt time!! */
      DevHelp_UnLock(SRBLockHandle);

      /* Place the SRB_LINK in the active queue of the ATE */
      InsertSRBActiveQueue(npATE,
                           npSRBLink,
                           (PASPI_SRB_HEADER) MAKEP(pDMWork->SRBGDTSelector,
                                                    0));

      /* If we are aborting a VDM request in response to the destruction */
      /* of the VDM itself, we will fake the VDM out to avoid any chance */
      /* that the VDM will have to wait a really long time for the command */
      /* to be aborted. This would occur if the device has disconnected */
      /* from the SCSI bus and will not see the abort request until the */
      /* command has completed and it reconnects to the bus. */
      if ((virtualASPI & VDM_DESTROY_EVENT) != 0)
         {
         /* Modify the IORB workspace of the SRB being aborted */
         /* to remember that the VDM was already destroyed. */
         pIORBToAbort = (PIORBH) pSRBToAbortWork->pIORB;
         ((PIORB_DMWORK) (pIORBToAbort->DMWorkSpace))->virtualASPI |= VDM_DESTROY_EVENT;

         /* Simulate the post to the VDD now. */
         CallVirtPostRoutine((PASPI_SRB_EXECUTE_IO) pSRBToAbortWork->pSRB);
         }

      /* Send the abort IORB to the ADD */
      SendIORB((PIORBH) pIORB,npATE->TargetADD_Entry);
      }
   }

/*********************************************************
*                                                        *
*   Procedure Name : DoASPISetAdapterParms               *
*                                                        *
*   Description : This procedure is intended to allow    *
*   adapter parameters to be modified and is therefore   *
*   quite vendor specific. Currently NOTHING is done.    *
*                                                        *
*   Input :                                              *
*         pSRAP - A pointer to the current SRB           *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
USHORT NEAR DoASPISetAdapterParms(PASPI_SRB_ADAPTER_PARMS pSRAP, USHORT virtualASPI)

   {
   pSRAP->SRBHdr.ASPIStatus = ASPI_STATUS_NO_ERROR;
   return(STDON);
   }

/*********************************************************
*                                                        *
*   Procedure Name : SRBToIORBPassThru                   *
*                                                        *
*   Description : This procedure creates an IORB from    *
*   the ASPI SRB and issues the command to the ADD.      *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the command target's ATE  *
*         pSRBIO - A pointer to the SRB                  *
*         npSRBLink - A pointer to the SRB_LINK that is  *
*           allocated for the current command.           *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR SRBToIORBPassThru(NPATE npATE,PASPI_SRB_EXECUTE_IO pSRBIO,
                            NPSRB_LINK npSRBLink,USHORT virtualASPI)

   {
   /* Allocate an IORB for the current SRB if one is not already available */
   if (!npSRBLink)
      npSRBLink = AllocateIORB(npATE,
                               (PASPI_SRB_HEADER) pSRBIO,
                               SRBToIORBPassThru,
                               virtualASPI);

   if (npSRBLink)
      {
      /* Create an IORB that the ADD can handle */
      BuildPassThruIORB(npATE,
                        pSRBIO,
                        npSRBLink,
                        virtualASPI);

      /* Place the SRB_LINK in the active queue of the ATE */
      InsertSRBActiveQueue(npATE,
                           npSRBLink,
                           (PASPI_SRB_HEADER) pSRBIO);

      /* Send the IORB to the ADD */
      SendIORB((PIORBH) &npSRBLink->IORB,
               npATE->TargetADD_Entry);
      }
   }

/*********************************************************
*                                                        *
*   Procedure Name : SRBToIORBReset                      *
*                                                        *
*   Description : This procedure creates an IORB that    *
*   will reset the selected adapter target.              *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the current ATE           *
*         pSRBRD - A pointer to the current SRB          *
*         npSRBLink - A pointer to the SRB_LINK that is  *
*           allocated for the current command.           *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR SRBToIORBReset(NPATE npATE,PASPI_SRB_RESET_DEVICE pSRBRD,
                         NPSRB_LINK npSRBLink,USHORT virtualASPI)

   {
   PIORB_DEVICE_CONTROL pIORB;
   PIORB_DMWORK pDMWork;
   PASPI_SRBWORK pASPIWork;
   ULONG physSRBIO;
   ULONG SRBLockHandle;

   /* Allocate an IORB for the current SRB if one is not already available */
   if (!npSRBLink)
      npSRBLink = AllocateIORB(npATE,
                               (PASPI_SRB_HEADER) pSRBRD,
                               SRBToIORBReset,
                               virtualASPI);

   if (npSRBLink)
      {
      /* Create an IORB that the ADD can handle */
      pIORB = (PIORB_DEVICE_CONTROL) &npSRBLink->IORB;
      pDMWork = (PIORB_DMWORK) &(pIORB->iorbh.DMWorkSpace);

      memset((PBYTE) pIORB,0,sizeof(IORB_DEVICE_CONTROL));
      pIORB->iorbh.Length = sizeof(IORB_DEVICE_CONTROL);
      pIORB->iorbh.UnitHandle = npATE->UnitHandle;
      pIORB->iorbh.CommandCode = IOCC_DEVICE_CONTROL;
      pIORB->iorbh.CommandModifier = IOCM_RESET;

      pIORB->iorbh.NotifyAddress = &NotifyIORBDone;
      pIORB->iorbh.StatusBlockLen = sizeof(SCSI_STATUS_BLOCK);
      pIORB->iorbh.pStatusBlock = (NPBYTE) &npSRBLink->commandStatus;

      /* Clear the status block before using it */
      memset((PBYTE) &npSRBLink->commandStatus,0,sizeof(SCSI_STATUS_BLOCK));

      /* Save DS and pointers to the request packet and current ATE */
      pDMWork->npATE = npATE;
      pDMWork->virtualASPI = virtualASPI;
      npSRBLink->OrigSRBPtr  = (PVOID) pSRBRD;

      /* Save the virtual pointer to the SRB and the IORB ptr */
      /* in the SRB workspace. These will be used if the request */
      /* originated in a VDM and the VDM is destroyed while the */
      /* request is still pending. */
      pASPIWork = (PASPI_SRBWORK) pSRBRD->ASPIWorkSpace;
      pASPIWork->pSRB = npSRBLink->OrigSRBPtr;
      pASPIWork->pIORB = (PIORBH) pIORB;

      /* Lock the SRB selector in place and record its location. */
      /* This really should have been done by the caller, but it is */
      /* nice to try and be sure. */
      DevHelp_Lock(SELECTOROF(pSRBRD),
                   1,
                   1,
                   &SRBLockHandle);

      DevHelp_VirtToPhys(pSRBRD,
                         &physSRBIO);

      pDMWork->SRBGDTSelector = npSRBLink->SRBGDTSelector;           /*@V58231*/
      DevHelp_PhysToGDTSelector(physSRBIO,
                                sizeof(ASPI_SRB_RESET_DEVICE),
                                pDMWork->SRBGDTSelector);

      /* Release our lock on the SRB because it cannot be done */
      /* at interrupt time!! */
      DevHelp_UnLock(SRBLockHandle);

      /* Place the SRB_LINK in the active queue of the ATE */
      InsertSRBActiveQueue(npATE,
                           npSRBLink,
                           (PASPI_SRB_HEADER) MAKEP(pDMWork->SRBGDTSelector,
                           0));

      /* Send the IORB to the ADD */
      SendIORB((PIORBH) pIORB,
               npATE->TargetADD_Entry);
      }
   }

/*********************************************************
*                                                        *
*   Procedure Name : FindAdapter                         *
*                                                        *
*   Description : This procedure scans the list of       *
*   host adapters that are accepting ASPI commands and   *
*   tries to find a match.                               *
*                                                        *
*   Input :                                              *
*         adapterIndex - The adapter being searched for  *
*                                                        *
*   Output :   A pointer to the ACB of the host adapter  *
*   that was found or a NULL pointer if there was no     *
*   match.                                               *
*                                                        *
*********************************************************/
NPACB FindAdapter(UCHAR adapterIndex)

   {
   NPACB   npACB;
   USHORT adapterCount;

   /* Start at the beginning of the ACB list */
   npACB = npACBAnchor;
   adapterCount = 0;

   /* Search until a match is found or all the adapters have been scanned */
   while ((npACB->AdapterIndex != adapterIndex) &&
          (adapterCount < numberOfASPIAdapters))
      {
      npACB = npACB->npNextACB;
      adapterCount++;
      }

   if (npACB->AdapterIndex != adapterIndex)
      npACB = 0;

   return(npACB);
   }

/*********************************************************
*                                                        *
*   Procedure Name : FindTarget                          *
*                                                        *
*   Description : This procedure scans the list of       *
*   targets attached to the current ACB and tries to     *
*   find a match.                                        *
*                                                        *
*   Input :                                              *
*         adapterIndex - The adapter being searched      *
*         targetID - The SCSI ID being searched for      *
*         targetLUN - The SCSI LUN being searched for    *
*                                                        *
*   Output :   A pointer to the ATE of the target device *
*   that was found or a NULL pointer if there was no     *
*   match.                                               *
*                                                        *
*********************************************************/
NPATE FindTarget(UCHAR adapterIndex,UCHAR targetID, UCHAR targetLUN)

   {
   NPACB npACB;
   NPATE npATE;

   /* Find the ACB of the target's host adapter and start searching */
   /* with the first target attached to it                          */
   npACB = FindAdapter(adapterIndex);
   npATE = npACB->npFirstATE;

   /* Search until the targetID is found or there are no more targets */
   while ((targetID != npATE->DeviceTargetID) &&
          (adapterIndex == npATE->npOwnerACB->AdapterIndex) &&
          (npATE))
      npATE = npATE->npNextATE;


   if ((targetID == npATE->DeviceTargetID) &&
       (adapterIndex == npATE->npOwnerACB->AdapterIndex) &&
       (npATE))
      {
      /* Search until the targetLUN is found or there are no more targets */
      while ((targetLUN != npATE->DeviceTargetLUN) &&
             (targetID == npATE->DeviceTargetID) &&
             (adapterIndex == npATE->npOwnerACB->AdapterIndex) &&
             (npATE))
         npATE = npATE->npNextATE;

      if ((targetLUN != npATE->DeviceTargetLUN) ||
          (targetID != npATE->DeviceTargetID) ||
          (adapterIndex != npATE->npOwnerACB->AdapterIndex))
         npATE = 0;
      }
   else
      /* No target was found so DON'T return a pointer */
      npATE = 0;
   return(npATE);
   }

#pragma optimize("cegl",off)        /* Disable optimization switches that    */
                                    /* are illegal for routines that contain */
                                    /* inline assembly code                  */

/*********************************************************
*                                                        *
*   Procedure Name : AllocateIORB                        *
*                                                        *
*   Description : This procedure takes the next          *
*   available SRB_LINK, which contains an IORB and       *
*   returns a pointer to it. If no IORB is available     *
*   the SRB will be inserted in a queue of waiting SRB   *
*   until all SRB_LINK required by it are available.     *
*                                                        *
*   Input :                                              *
*                                                        *
*   Output :   A pointer to the next available SRB_LINK  *
*              or 0 if there are an insufficient number  *
*              of SRB_LINK available for the request.    *
*                                                        *
*********************************************************/
NPSRB_LINK AllocateIORB(NPATE npATE, PASPI_SRB_HEADER pSRBH,
                        VOID (NEAR *ASPIRequestRoutine)(), USHORT virtualASPI)

   {
   NPSRB_LINK npSRBLink = 0;

   /* If an SRB_LINK is available return a pointer to it       */
   /* otherwise place the SRB in the queue of waiting requests */

   DISABLE

   if (npSRBLinkFreeList)
      {
      npSRBLink = npSRBLinkFreeList;
      npSRBLinkFreeList = npSRBLinkFreeList->npNextSRBLink;
      npSRBLink->OrigSRBPtr = (PVOID) pSRBH;
      }
   else
      {
      SRBWaitQueue[IORBWaitSem].SRBPtr = pSRBH;                       /*@V61092*/
      SRBWaitQueue[IORBWaitSem].ATEPtr = npATE;                       /*@V61092*/
      SRBWaitQueue[IORBWaitSem].callbackFunction = ASPIRequestRoutine;/*@V61092*/
      SRBWaitQueue[IORBWaitSem].virtualASPI = virtualASPI;
      IORBWaitSem++;                                                  /*@V61092*/
      }                                                               /*@V61092*/

   ENABLE

   return(npSRBLink);
   }

/*********************************************************
*                                                        *
*   Procedure Name : FreeIORB                            *
*                                                        *
*   Description : This procedure releases the IORB or    *
*   that was just completed and places it back in the    *
*   pool of available SRB_LINKs. If SRBs are waiting,    *
*   the next one will be started.                        *
*                                                        *
*   Input : A pointer to the SRB_LINK(s)                 *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID FreeIORB(NPSRB_LINK npSRBLink)

   {
   DISABLE                                                           /*@V61092*/
                                                                     /*@V61092*/
   /* If anyone is waiting for an IORB, wake them up */              /*@V61092*/
   if (IORBWaitSem)                                                  /*@V61092*/
      {                                                              /*@V61092*/
      IORBWaitSem--;                                                 /*@V61092*/
                                                                     /*@V61092*/
      npSRBLink->OrigSRBPtr = (PVOID) (SRBWaitQueue[IORBWaitSem].SRBPtr);
      /* Resubmit SRB with SRB_LINK already allocated */             /*@V61092*/
      (VOID) (SRBWaitQueue[IORBWaitSem].callbackFunction)            /*@V61092*/
               (SRBWaitQueue[IORBWaitSem].ATEPtr,                    /*@V61092*/
                SRBWaitQueue[IORBWaitSem].SRBPtr,                    /*@V61092*/
                npSRBLink,
                SRBWaitQueue[IORBWaitSem].virtualASPI);
      ENABLE                                                         /*@V61092*/
      }                                                              /*@V61092*/
   else                                                              /*@V61092*/
      {                                                              /*@V61092*/
      /* Insert the completed IORB at the front of the free list */  /*@V61092*/
      npSRBLink->npNextSRBLink = npSRBLinkFreeList;                  /*@V61092*/
      npSRBLinkFreeList = npSRBLink;                                 /*@V61092*/
      ENABLE                                                         /*@V61092*/
      }                                                              /*@V61092*/
   }

/* Removed optimization from this routine because of compiler */     /*@V58231*/
/*     in MSC 6.00A.04.                                       */     /*@V58231*/
#pragma optimize("gl",on)    /* Enable optimization switches again *//*@V58231*/

/*********************************************************
*                                                        *
*   Procedure Name : BuildPassThruIORB                   *
*                                                        *
*   Description : This procedure builds an IORB for the  *
*   current SRB within a freshly allocated SRB_LINK.     *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the current ATE           *
*         pSRBIO - A pointer to the current SRB          *
*         npSRBLink - A pointer to the new SRB_LINK      *
*         virtualASPI - Flags indicating the status of   *
*           the VDM that MAY be associated with the SRB. *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID BuildPassThruIORB(NPATE npATE,PASPI_SRB_EXECUTE_IO pSRBIO,
                       NPSRB_LINK npSRBLink, USHORT virtualASPI)

   {
   PIORB_ADAPTER_PASSTHRU pIORB;
   PIORB_DMWORK pDMWork;
   PASPI_SRBWORK pASPIWork;

   pIORB = (PIORB_ADAPTER_PASSTHRU) &npSRBLink->IORB;
   pDMWork = (PIORB_DMWORK) &(pIORB->iorbh.DMWorkSpace);

   memset((PBYTE) pIORB,
          0,
          sizeof(IORB_ADAPTER_PASSTHRU));
   pIORB->iorbh.Length = sizeof(IORB_ADAPTER_PASSTHRU);
   pIORB->iorbh.UnitHandle = npATE->UnitHandle;
   pIORB->iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
   pIORB->iorbh.CommandModifier = IOCM_EXECUTE_CDB;

   npSRBLink->OrigSRBPtr  = (PVOID) pSRBIO;

   /* Save the virtual pointer to the SRB and the IORB ptr */
   /* in the SRB workspace. These will be used if the request */
   /* originated in a VDM and the VDM is destroyed when the */
   /* request is still pending. */
   pASPIWork = (PASPI_SRBWORK) pSRBIO->ASPIWorkSpace;
   pASPIWork->pSRB = npSRBLink->OrigSRBPtr;
   pASPIWork->pIORB = (PIORBH) pIORB;

   /* Copy the SRB selector to the GDT so we can use it later */
   pDMWork->SRBGDTSelector = npSRBLink->SRBGDTSelector;              /*@V58231*/
   DevHelp_PhysToGDTSelector(pSRBIO->ppSRB,
                             sizeof(ASPI_SRB_EXECUTE_IO) +
                             pSRBIO->SenseDataLen +
                             pSRBIO->SCSICDBLen,
                             pDMWork->SRBGDTSelector);

   /* Convert the SRB pointer to the "safe" GDT selector */
   pSRBIO = (PASPI_SRB_EXECUTE_IO) MAKEP(pDMWork->SRBGDTSelector,0);

   pIORB->iorbh.NotifyAddress = &NotifyIORBDone;
   pIORB->ControllerCmdLen = pSRBIO->SCSICDBLen;
   pIORB->pControllerCmd = pSRBIO->SCSICDBStart;
   pIORB->iorbh.StatusBlockLen = sizeof(SCSI_STATUS_BLOCK);
   pIORB->iorbh.pStatusBlock = (NPBYTE) &npSRBLink->commandStatus;

   /* Clear the status block before using it */
   memset((PBYTE) &npSRBLink->commandStatus,0,sizeof(SCSI_STATUS_BLOCK));

   ((PSCSI_STATUS_BLOCK)(pIORB->iorbh.pStatusBlock))->SenseData =
      (PSCSI_REQSENSE_DATA) (pSRBIO->SCSICDBStart + pSRBIO->SCSICDBLen);

   ((PSCSI_STATUS_BLOCK)(pIORB->iorbh.pStatusBlock))->ReqSenseLen =
      pSRBIO->SenseDataLen;

   /* Set up a pointer to the scatter/gather list or data buffer */
   if (pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_SG_ENABLE)
      {
      pIORB->cSGList = pSRBIO->SGListLen;
      pIORB->ppSGLIST = pSRBIO->ppDataBuffer;

      /* Get a pointer to the scatter/gather list by using the GDT */
      /* allocated to the current target for scatter/gather.       */
      pDMWork->SGGDTSelector = npSRBLink->SGGDTSelector;             /*@V58231*/
      DevHelp_PhysToGDTSelector(pIORB->ppSGLIST,
                                pIORB->cSGList * sizeof(SCATGATENTRY),
                                pDMWork->SGGDTSelector);             /*@V58231*/

      pIORB->pSGList = (PSCATGATENTRY) MAKEP(pDMWork->SGGDTSelector,0);/*@V61092*/
      }
   else
      {
      /* Treat a standard data buffer as a scatter/gather list     */
      /* with only one element. If there is no data transfer, then */
      /* do NOT build a scatter/gather list.                       */
      if (pSRBIO->DataXferLen)                                       /*@V58231*/
         {                                                           /*@V58231*/
         pIORB->cSGList = 1;                                         /*@V58231*/
         pIORB->pSGList = (PVOID) pIORB;
         OFFSETOF(pIORB->pSGList) = (USHORT)((ULONG)&(pDMWork->SGList));
         pIORB->ppSGLIST = (ULONG)  (ppDataSeg +
                                    (USHORT)((ULONG) &(pDMWork->SGList)));

         /* Insert the single element in the scatter/gather list */
         (ULONG) (pDMWork->SGList.ppXferBuf) = pSRBIO->ppDataBuffer;
         (ULONG) (pDMWork->SGList.XferBufLen) = pSRBIO->DataXferLen;
         }                                                           /*@V58231*/
      }

   /* Give all commands the MAXIMUM timeout since ASPI does not   */
   /* really require this option. Commands that don't finish will */
   /* be aborted by the driver that requested them.               */
   pIORB->iorbh.Timeout = 0xFFFFFFFF;

   /* Record the direction of the data transfer */
   DetermineDirectionBits(npATE, pIORB, pSRBIO);

   /* Save pointer to the current ATE */
   pDMWork->npATE = npATE;
   pDMWork->virtualASPI = virtualASPI;
   }

#pragma optimize("ce",on)        /* Enable optimization switches */  /*@V58231*/

/*********************************************************
*                                                        *
*   Function Name : DetermineDirectionBits               *
*                                                        *
*   Description : This function determines command       *
*   direction and inserts the appropriate bits into      *
*   the IORB.                                            *
*                                                        *
*   Input :                                              *
*         pSRBIO - A pointer to the SRB being processed  *
*         pIORB - A pointer to the IORB being created    *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
USHORT DetermineDirectionBits( NPATE                  npATE,
                               PIORB_ADAPTER_PASSTHRU pIORB,
                               PASPI_SRB_EXECUTE_IO   pSRBIO )
   {
   USHORT   OpCode = pSRBIO->SCSICDBStart[0];
   USHORT   rc = 1;
   NPUCHAR  npDIRM;
   NPUCHAR  npDIREX;
   USHORT   DirBits;
   USHORT   i, n;

   /**
    ** Was the direction of the command given in the SRB?
    **/

   DirBits = pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_DIRECTION_BITS;

   if ( (DirBits == ASPI_REQFLAG_DIR_TO_HOST)  ||
        (DirBits == ASPI_REQFLAG_DIR_NO_DATA_XFER ) )
      {
      pIORB->Flags = PT_DIRECTION_IN;
      }
   else if ( DirBits == ASPI_REQFLAG_DIR_TO_TARGET )
      {
      pIORB->Flags = 0;
      }
   else
      {
      /**
       ** Determine the transfer direction based on the CDB
       **
       ** The appropriate direction mask is determined at INIT
       ** time. If the device does not have a known SCSI device
       ** type and we do not have a specific direction mask for
       ** it, then the ASPI client driver must specify direction!
       **
       ** The table is encodeded so that a 1-bit indicates
       ** direction of transfer is Initiator-to-Target,
       ** i.e. a WRITE to the target.
       **/
      if ( npDIREX = npATE->npDirExcept )
         {
         n = npDIREX[0] * 2 + 1;
         for (i=1; i < n; i+=2 )
            {
            if ( OpCode == npDIREX[i] )
               {
               pIORB->Flags = ( npDIREX[i+1] ) ? 0 : PT_DIRECTION_IN;
               goto Exit_Dir;
               }
            }
         }
      if ( npDIRM = npATE->npDirTable )
         {
         pIORB->Flags = ( npDIRM[OpCode/8] & (0x80>>(OpCode % 8)) )
                                                    ? 0: PT_DIRECTION_IN;
         }
      else
         {
           rc = 0;
         }

      }

Exit_Dir:
   return rc;
   }

/*********************************************************
*                                                        *
*   Function Name : SendIORB                             *
*                                                        *
*   Description : This function sends an IORB to the     *
*   ADD and returns immediately after submitting it.     *
*                                                        *
*   Input :                                              *
*         npIORB - A pointer to the IORB being issued    *
*         pADD_IORB_Entry - A pointer to the ADD         *
*            entry point for the unit receiving the IORB *
*                                                        *
*   Output :   This function returns the error status    *
*   reported by the IORB.                                *
*                                                        *
*********************************************************/
VOID SendIORB(PIORBH pIORB, VOID (FAR *pADD_IORB_Entry)(PIORB))

   {
   pIORB->RequestControl = IORB_ASYNC_POST | IORB_REQ_STATUSBLOCK |
                           IORB_DISABLE_RETRY;

   (*pADD_IORB_Entry)(pIORB);
   }


#pragma optimize("cegl",off)        /* Disable optimization switches that    */
                                    /* are illegal for routines that contain */
                                    /* inline assembly code                  */

/*********************************************************
*                                                        *
*   Function Name : SendIORBandWait                      *
*                                                        *
*   Description : This function sends an IORB to the     *
*   ADD and waits for completion before returning.       *
*                                                        *
*   Input :                                              *
*         npIORB - A pointer to the IORB being issued    *
*         pADD_IORB_Entry - A pointer to the ADD         *
*            entry point                                 *
*                                                        *
*   Output :   This function returns the error status    *
*   reported by the IORB.                                *
*                                                        *
*********************************************************/
USHORT SendIORBandWait(NPIORBH npIORB, VOID (FAR *pADD_IORB_Entry)(PIORB))

   {
   npIORB->RequestControl = IORB_ASYNC_POST;

   (*pADD_IORB_Entry)(npIORB);

   DISABLE
   while (!(npIORB->Status & IORB_DONE))
      {
      DevHelp_ProcBlock((ULONG) OFFSETOF(npIORB),
                        (ULONG) -1,
                        WAIT_IS_NOT_INTERRUPTABLE);
      DISABLE
      }
   ENABLE

   return(npIORB->ErrorCode);
   }

/*********************************************************
*                                                        *
*   Procedure Name : NotifyIORBDone                      *
*                                                        *
*   Description : This procedure is called by the ADD    *
*   when it has completed processing the IORB.           *
*                                                        *
*   Input :                                              *
*         fpIORB - A far pointer to the IORB that has    *
*            just been completed                         *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID FAR _loadds NotifyIORBDone(PIORB fpIORB)                        /*@V61779*/

   {
   PASPI_SRB_EXECUTE_IO pSRBIO;
   NPSRB_LINK npSRBLink;
   PIORB_DMWORK pWorkSpace;
   NPATE npATE;

   /* Restore DS and pointer to current ATE */
   pWorkSpace = (PIORB_DMWORK) fpIORB->DMWorkSpace;
   npATE = pWorkSpace->npATE;

   npSRBLink = (NPSRB_LINK) OFFSETOF(fpIORB);
   ((NPBYTE) npSRBLink) -= ((NPBYTE)&npSRBLink->IORB - (NPBYTE)npSRBLink);

   pSRBIO = (PASPI_SRB_EXECUTE_IO) MAKEP(pWorkSpace->SRBGDTSelector,0);

   /* Immediately verify that this request does not belong to VDM */
   /* that no longer exists. */
   if ((pWorkSpace->virtualASPI & VDM_DESTROY_EVENT) != 0)
      {
      /* Deallocate the target if we are sharing it with another device */
      /* manager and we allocated it for our request. This will impact */
      /* performance slightly when /SHARE is used, but it is necessary to */
      /* avoid leaving a device unaccessible to another manager. */
      if ((npATE->Flags & ATEF_SHARE_ALLOCATION) &&
          ((pSRBIO->SRBHdr.CommandCode == ASPI_CMD_EXECUTE_IO) ||
           (pSRBIO->SRBHdr.CommandCode == ASPI_CMD_RESET_DEVICE)))
         SendUnitControlCommand(npATE,
                                IOCM_DEALLOCATE_UNIT);

      /* The command now being handled is either a VDM request or an abort */
      /* of a VDM request. The former was posted to the VDD before sending */
      /* the latter. The latter is actually a standard OS/2 request and */
      /* does not need to be posted. However, the OS/2 request does need */
      /* to be marked complete. */

      /* Create error the error status for the OS/2 request */
      if ((pWorkSpace->virtualASPI & VDM_REQUEST) == 0)
         ConvertIORBError((PIORBH) fpIORB,
                           pSRBIO);

      /* Clean up the queues and get out!! */
      DeleteSRBActiveQueue(npATE,
                           npSRBLink);
      FreeIORB(npSRBLink);
      }
   else
      {
      /* Create error messages for ASPI from the IORB errors */
      ConvertIORBError((PIORBH) fpIORB,
                       pSRBIO);

      /* Update data transfer length if residual byte count was requested *//*@V53040*/
      if (pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_RESIDUAL)              /*@V53040*/
         {
         /* We must ALWAYS return the residual byte count reported because */
         /* certain host adapters only detect OVERRUNs.                    *//*@V53040*/
         pSRBIO->DataXferLen =
            ((PSCSI_STATUS_BLOCK)((PIORBH) fpIORB->pStatusBlock))->ResidualLength;
         }

      /* Deallocate the target if we are sharing it with another device */
      /* manager and we allocated it for our request. This will impact */
      /* performance slightly when /SHARE is used, but it is necessary to */
      /* avoid leaving a device unaccessible to another manager. */
      if ((npATE->Flags & ATEF_SHARE_ALLOCATION) &&
          ((pSRBIO->SRBHdr.CommandCode == ASPI_CMD_EXECUTE_IO) ||
           (pSRBIO->SRBHdr.CommandCode == ASPI_CMD_RESET_DEVICE)))
         SendUnitControlCommand(npATE,
                                IOCM_DEALLOCATE_UNIT);

      /* Submit the next linked SRB (if necessary) */
      if (pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_LINKED_SRB)
         SRBToIORBPassThru(npATE,                                       /*@V61092*/
                           (PASPI_SRB_EXECUTE_IO) pSRBIO->ppNxtSRB,
                           0,
                           pWorkSpace->virtualASPI);

      /* Call the ASPI posting routine (if necessary) */
      if (pWorkSpace->virtualASPI & VDM_REQUEST)
            CallVirtPostRoutine(npSRBLink->OrigSRBPtr);
      else
         if (pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_POST_ENABLE)
            CallPostRoutine(pSRBIO);                                    /*@V61092*/

      /* Remove the SRB_LINK from the active queue of the ATE */
      /* and place it back in the list of free IORB           */
      DeleteSRBActiveQueue(npATE,
                           npSRBLink);
      FreeIORB(npSRBLink);
      }
   }
#pragma optimize("cegl",on)         /* Enable optimization switches again */

/******************************************************
*                                                     *
*   Procedure Name : ConvertIORBError                 *
*                                                     *
*   Description : This procedure converts any error   *
*   returned in the IORB to an ASPI error code and    *
*   inserts it in the SRB.                            *
*                                                     *
*   Input :                                           *
*         pIORB - A pointer to the IORB that has just *
*            been completed                           *
*         pSRBIO - A pointer to the SRB that owns the *
*            IORB that was just completed             *
*                                                     *
*   Output :                                          *
*                                                     *
******************************************************/
VOID ConvertIORBError(PIORBH pIORBH,PASPI_SRB_EXECUTE_IO pSRBIO)

   {
   /* Was there an error detected by the ADD? */
   if (pIORBH->Status & IORB_ERROR)
      {
      /* What is the general error condition? */
      switch (pIORBH->ErrorCode)
         {
         case IOERR_CMD_ABORTED :
            pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_ABORTED;
            break;

         case IOERR_CMD_NOT_SUPPORTED :
            pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_INVALID_COMMAND;
            break;

         default :
            pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_ERROR;
         }


      /* Does the SRB support adapter status and target status? */
      if (pSRBIO->SRBHdr.CommandCode == ASPI_CMD_EXECUTE_IO)
         {
         /* Did the host adapter detect an error? */
         switch (pIORBH->ErrorCode)
            {
            case IOERR_ADAPTER_TIMEOUT:
            case IOERR_ADAPTER_DEVICE_TIMEOUT:
               pSRBIO->HostStatus = ASPI_HSTATUS_SELECTION_TIMEOUT;
               break;
            case IOERR_ADAPTER_OVERRUN:
            case IOERR_ADAPTER_UNDERRUN:
            case IOERR_DEVICE_OVERRUN:
            case IOERR_DEVICE_UNDERRUN:
               pSRBIO->HostStatus = ASPI_HSTATUS_DATA_OVERRUN;
               break;
            case IOERR_ADAPTER_HOSTBUSCHECK:
            case IOERR_ADAPTER_DEVICEBUSCHECK:
               pSRBIO->HostStatus = ASPI_HSTATUS_BUS_PHASE_ERROR;
               break;
            case IOERR_CMD_SGLIST_BAD:                                  /*@V53040*/
               pSRBIO->HostStatus = ASPI_HSTATUS_BAD_SGLIST;            /*@V53040*/
               break;                                                   /*@V53040*/
            default:

               /* If residual byte count was requested in the SRB we must */
               /* make sure that an underrun is reported when there is    */
               /* a valid residual byte count in the status block         */
               if ((pSRBIO->SRBHdr.ASPIReqFlags & ASPI_REQFLAG_RESIDUAL) &&
                   (((PSCSI_STATUS_BLOCK) (pIORBH->pStatusBlock))->Flags &
                     STATUS_RESIDUAL_VALID) &&
                   (((PSCSI_STATUS_BLOCK) (pIORBH->pStatusBlock))->ResidualLength))
                  pSRBIO->HostStatus = ASPI_HSTATUS_DATA_OVERRUN;
               else
                  pSRBIO->HostStatus = ASPI_HSTATUS_NO_ERROR;
            }

         /* Did the target detect an error? */
         switch (pIORBH->ErrorCode)
            {
            case IOERR_DEVICE_BUSY:
               pSRBIO->TargetStatus = ASPI_TSTATUS_BUSY;
               break;
            case IOERR_UNIT_NOT_ALLOCATED:
               pSRBIO->TargetStatus = ASPI_TSTATUS_RESERV_CONFLICT;
               break;
            default:

               /* If there was sense data returned then record check condition */
               if (((PSCSI_STATUS_BLOCK) (pIORBH->pStatusBlock))->Flags &
                    STATUS_SENSEDATA_VALID)
                  pSRBIO->TargetStatus = ASPI_TSTATUS_CHECK_CONDITION;
               else
                  pSRBIO->TargetStatus = ASPI_TSTATUS_NO_ERROR;

            }
         }
      }
   else
      {
      /* There was no error detected */
      pSRBIO->SRBHdr.ASPIStatus = ASPI_STATUS_NO_ERROR;

      /* Does the SRB support adapter status and target status? */
      if (pSRBIO->SRBHdr.CommandCode == ASPI_CMD_EXECUTE_IO)
         {
         pSRBIO->TargetStatus = ASPI_TSTATUS_NO_ERROR;
         pSRBIO->HostStatus = ASPI_HSTATUS_NO_ERROR;
         }
      }
   }

#pragma optimize("cegl",off)        /* Disable optimization switches that    */
                                    /* are illegal for routines that contain */
                                    /* inline assembly code                  */

/*********************************************************
*                                                        *
*   Function Name : CallPostRoutine                      *
*                                                        *
*   Description : This function calls the post routine   *
*   of the driver whose request has just completed.      *
*                                                        *
*   Input :                                              *
*         pSRBIO - A pointer to the SRB for I/O          *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID CallPostRoutine(PASPI_SRB_EXECUTE_IO pSRBIO)

   {
   /* Save the data segment since posting will trash it */

   _asm push ds
   _asm push es
   _asm push si
   _asm push di
   (*pSRBIO->PM_PostAddress)(pSRBIO->PM_DataSeg,
                             (PASPI_SRB_HEADER) pSRBIO->ppSRB);
   _asm pop di
   _asm pop si
   _asm pop es
   _asm pop ds
   }

/*********************************************************
*                                                        *
*   Function Name : SendUnitControlCommand               *
*                                                        *
*   Description : This function reserves/frees the       *
*   current target by issuing the appropriate IORB       *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the targets's ATE         *
*         unitControlCommand - The command to send       *
*                                                        *
*   Output :   This function returns 0 if the command was*
*   successfully sent and a positive integer if an       *
*   error was detected.                                  *
*                                                        *
*********************************************************/
USHORT SendUnitControlCommand(NPATE npATE,USHORT unitControlCommand)

   {
   USHORT resultCode;
   NPIORB_UNIT_CONTROL npIORBUnitControl = (NPIORB_UNIT_CONTROL) npATE->UnitControlIORB; /*@V64399*/
   PIORB_DMWORK pDMWork = (PIORB_DMWORK) (npIORBUnitControl->iorbh.DMWorkSpace);         /*@V64399*/

   memset((PBYTE) npIORBUnitControl,
          0,
          sizeof(IORB_UNIT_CONTROL));
   npIORBUnitControl->iorbh.Length          = sizeof(IORB_UNIT_CONTROL);
   npIORBUnitControl->iorbh.UnitHandle      = npATE->UnitHandle;
   npIORBUnitControl->iorbh.CommandCode     = IOCC_UNIT_CONTROL;
   npIORBUnitControl->iorbh.CommandModifier = unitControlCommand;
   npIORBUnitControl->iorbh.Status          = 0;
   npIORBUnitControl->iorbh.ErrorCode       = 0;
   npIORBUnitControl->Flags                 = 0;
   npIORBUnitControl->iorbh.NotifyAddress = &AllocationPost;         /*@V64399*/
                                                                     /*@V64399*/
   /* Save a pointer to the current ATE */                           /*@V64399*/
   pDMWork->npATE = npATE;                                           /*@V64399*/

   resultCode = SendIORBandWait((NPIORB) npIORBUnitControl,
                                npATE->TargetADD_Entry);

   return(resultCode);
   }

/*********************************************************
*                                                        *
*   Procedure Name : InsertSRBActiveQueue                *
*                                                        *
*   Description : This procedure places the SRB link     *
*   at the front of the queue of active IORBs that       *
*   belong to the current ATE.                           *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the current ATE           *
*         npSRBLink - A pointer to the new SRB link      *
*         pSRB - A pointer to the current SRB            *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID InsertSRBActiveQueue(NPATE npATE,NPSRB_LINK npSRBLink,PASPI_SRB_HEADER pSRBH)

   {
   /* Place the new SRB at the front of the queue */
   DISABLE

   npSRBLink->pSRB = pSRBH;
   npATE->SRBActiveQueueCount++;
   npSRBLink->npNextSRBLink = npATE->npSRBActiveQueue;
   npATE->npSRBActiveQueue = npSRBLink;
   ENABLE
   }

/*********************************************************
*                                                        *
*   Procedure Name : DeleteSRBActiveQueue                *
*                                                        *
*   Description : This procedure deletes the SRB link    *
*   for the IORB that has just been completed from the   *
*   active queue of its ATE.                             *
*                                                        *
*   Input :                                              *
*         npATE - A pointer to the current ATE           *
*         npSRBLink - A pointer to the SRB link that     *
*            has just been completed                     *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID DeleteSRBActiveQueue(NPATE npATE,NPSRB_LINK npSRBLink)

   {
   USHORT SRBCount = 0;
   NPSRB_LINK currentSRBLink;
   NPSRB_LINK previousSRBLink;

   /* Start at the front of the queue */
   currentSRBLink = previousSRBLink = npATE->npSRBActiveQueue;

   DISABLE
   while (SRBCount < npATE->SRBActiveQueueCount)
      {
      /* Is this the correct queue element to delete? */
      if (currentSRBLink->pSRB == npSRBLink->pSRB)
         {
         /* If this is the only element on the queue, then */        /*@V61092*/
         /* clear the whole queue.                         */        /*@V61092*/
         if ((!SRBCount) &&                                          /*@V61092*/
             (!previousSRBLink->npNextSRBLink))                      /*@V61092*/
            npATE->npSRBActiveQueue = 0;                             /*@V61092*/
         else                                                        /*@V61092*/
            previousSRBLink->npNextSRBLink = currentSRBLink->npNextSRBLink;
         break;
         }
      else
         {
         /* No match yet, so try the next element in the queue */
         previousSRBLink = currentSRBLink;
         currentSRBLink = currentSRBLink->npNextSRBLink;
         SRBCount++;
         }
      }
   npATE->SRBActiveQueueCount--;

   ENABLE
   }

#pragma optimize("cegl",on)         /* Enable optimization switches again */

/*********************************************************
*                                                        *
*   Procedure Name : AllocationPost                      *
*                                                        *
*   Description : This procedure is called by the ADD    *
*   whenever an allocation or deallocation request has   *
*   completed.                                           *
*                                                        *
*   Input :                                              *
*         pIORB - A far pointer to the IORB that has     *
*            just been completed                         *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID FAR _loadds AllocationPost(PIORB pIORB)                         /*@V64399*/
                                                                     /*@V64399*/
   {                                                                 /*@V64399*/
   NPATE npATE;                                                      /*@V64399*/
   PIORB_DMWORK pDMWork;                                             /*@V64399*/
                                                                     /*@V64399*/
   /* If the command completed successfully update our structures. *//*@V64399*/
   if (((PIORB_UNIT_CONTROL) pIORB)->iorbh.Status == IORB_DONE)      /*@V64399*/
      {                                                              /*@V64399*/
      /* Get a pointer to the appropriate ATE */                     /*@V64399*/
      pDMWork = (PIORB_DMWORK) &(((PIORB_UNIT_CONTROL) pIORB)->iorbh.DMWorkSpace);/*@V64399*/
      npATE = pDMWork->npATE;                                        /*@V64399*/
                                                                     /*@V64399*/
      /* If we are doing an allocate or deallocate we must set the *//*@V64399*/
      /* appropriate bits in the ATE, when the request finishes.   *//*@V64399*/
      if (pIORB->CommandCode == IOCC_UNIT_CONTROL)                   /*@V64399*/
         switch (pIORB->CommandModifier)                             /*@V64399*/
            {                                                        /*@V64399*/
            case IOCM_ALLOCATE_UNIT:                                 /*@V64399*/
               npATE->Flags |= ATEF_ASPI_ALLOCATED;
               break;                                                /*@V64399*/
                                                                     /*@V64399*/
            case IOCM_DEALLOCATE_UNIT:                               /*@V64399*/
               npATE->Flags &= ~ATEF_ASPI_ALLOCATED;
               break;                                                /*@V64399*/
            }                                                        /*@V64399*/
      }                                                              /*@V64399*/
   }                                                                 /*@V64399*/

