/*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/os2scsi/scgioctl.c, scsy, ddk_subset, b_bdd.032 93/10/25";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = SCGIOCTL.C
 *
 * DESCRIPTIVE NAME = OS2SCSI.DMD - OS/2 SCSI.SYS Emulation
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : OS2SCSI (SCSI.SYS) IOCTL processing
 *
 *
 *
*/
#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"
#include "error.h"
#include "strat2.h"
#include "reqpkt.h"
#include "dhcalls.h"
#include "scb.h"
#include "iorb.h"
#include "scsi.h"
#include "scscsi.h"
#include "scgen.h"
#include "scproto.h"
#include "abios.h"


extern UNITCB     UnitCB[1];               /* First UnitCB allocated here    */
extern USHORT     NumUnitCBs;              /* number of unit control blocks  */
extern UCHAR      ScratchBuffer1[512];     /* Scratch buffer for I/O         */
extern USHORT     vTimeout_buff;           /* timeout value buffer           */
extern UCHAR      ControllerClass[];       /* Controller Class ID for IORB   */


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  f_DriveGenIOCtl                                         *
*                                                                           *
* DESCRIPTIVE NAME: Far General I/O Control                                 *
*                                                                           *
* FUNCTION:         This routine handles Generic I/O Control request.       *
*                   It checks the function in the parameter packet, and     *
*                   call to the appropriate subroutine to process it.       *
*                                                                           *
* ENTRY POINT:      f_DriveGenIOCtl                                         *
*                                                                           *
* LINKAGE:          Call Far                                                *
*                                                                           *
* INPUT:            pGenioctl Pointer to General I/O Control Request Packet *
*                                                                           *
* EXIT-NORMAL:                                                              *
*                                                                           *
* EXIT-ERROR:                                                               *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT far f_DriveGenIOCtl(EntryType, pRPH)
USHORT EntryType;
PRPH   pRPH;
{
  PRP_GENIOCTL   pGenioctl = (PRP_GENIOCTL)pRPH;
  PSCSI_IN       pParm = (PSCSI_IN) (pGenioctl->ParmPacket);
  PSCSI_OUT      pData = (PSCSI_OUT)(pGenioctl->DataPacket);
  USHORT         iUnit;
  USHORT         rc = STDON | STERR | ERROR_I24_BAD_COMMAND;
  NPUCB          npUCB;

  if (pGenioctl->Category == ABIOS_SERVICE)
  {
     if ((pGenioctl->Function != (ABFC_SCSIP_ALLOC_SCSI_DEV | SCSI_PLUS_VALUE)) &&
         (pGenioctl->Function != (ABFC_SCSIP_RET_TYPE_COUNT | SCSI_PLUS_VALUE)))
     {
        iUnit = pParm->hDev;
                                           /* Device Handle Check */
        if ( iUnit > (NumUnitCBs - 1))
        {
           rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
           return ( rc );
        }
        npUCB = &UnitCB[iUnit];
                                           /* Already allocated or not */
        if (!(npUCB->IntUnitFlags & IUF_ALLOCATED))
        {
           rc = STDON + STERR + ERROR_I24_BAD_UNIT;
           return ( rc );
        }
     }
                                           /* Distribute function */
     switch (pGenioctl->Function)
     {
        case (ABFC_READ_DEVICE_PARMS | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Read Device Parameters                */
           /*****************************************/

           WaitOtherIORB( npUCB );
           rc = ReadDevParms(pGenioctl);
           ReleaseOtherIORB( npUCB );

           break;


        case (ABFC_RESET_DEVICE | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Reset / Initialize                    */
           /*****************************************/
                                           /* Queueing Command */
           if((rc = WaitIORB(pGenioctl,EntryType)) == TRUE)
           {
              rc = Reset_Init(pGenioctl);
              ReleaseIORB(pGenioctl);
              return(rc);
           }
           else
              if (rc == REQ_FLUSHED)
                 return(STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA);
              else if (rc == REQ_INTERRUPTED)
                 return(STDON + STERR + SCSI_ERR_UNUSUAL_WAKEUP);
              else
                 return(STDON + STERR + SCSI_ERR_DEVHELP);


        case (ABFC_SCSIA_ENABLE_CACHE | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Enable Intelligent Buffer             */
           /*****************************************/

           return(STDON + STERR + SCSI_ERR_INT_BUFF_NOT_SUPPORT);


        case (ABFC_SCSIA_DISABLE_CACHE | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Disable Intelligent Buffer            */
           /*****************************************/

           return(STDON + STERR + SCSI_ERR_INT_BUFF_NOT_SUPPORT);


        case (ABFC_SCSIA_CACHE_STATUS | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Return Intelligent Buffer Status      */
           /*****************************************/

           return(STDON + STERR + SCSI_ERR_INT_BUFF_NOT_SUPPORT);


        case (ABFC_SCSIP_SET_DEV_TIMEOUT | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Set Device Timeout                    */
           /*****************************************/

                                           /* Immediate Command */
           return(SetTimeout(pGenioctl));


        case (ABFC_SCSIP_READ_DEV_TIMEOUT | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Read Device Timeout                   */
           /*****************************************/

                                           /* Immediate Command */
           return(ReadTimeout(pGenioctl));


        case (ABFC_SCSIP_TRANSFER_SCB | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Transfer SCB                          */
           /*****************************************/

           if ((rc = WaitIORB(pGenioctl, EntryType)) == TRUE )
           {
              rc = XferSCB(pGenioctl);
              ReleaseIORB(pGenioctl);
           }
           else
              if (rc == REQ_FLUSHED)
                 rc = STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA;
              else if (rc == REQ_INTERRUPTED)
                 rc = STDON + STERR + SCSI_ERR_UNUSUAL_WAKEUP;
              else
                 rc = STDON + STERR + SCSI_ERR_DEVHELP;

           break;


        case (ABFC_SCSIP_DEALLOC_SCSI_DEV | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Deallocate Device                     */
           /*****************************************/

           WaitOtherIORB( npUCB );
           rc = DeallocDev(pGenioctl);
           ReleaseOtherIORB( npUCB );

           break;


        case (ABFC_SCSIP_ALLOC_SCSI_DEV | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Allocate Device                       */
           /*****************************************/

           /*---------------------------------------------------*/
           /* Note: This routine requires the 'OtherIORB'       */
           /*       however we do not know which UCB will be    */
           /*       addressed. WaitOtherIORB() is called within */
           /*       this routine when a UCB has been selected.  */
           /*---------------------------------------------------*/

           rc = AllocDev(pGenioctl);

           break;

        case (ABFC_SCSIP_RET_TYPE_COUNT | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Return Peripheral Type Count          */
           /*****************************************/
                                           /* Immediate Command */
           rc = RetTypeCnt(pGenioctl);

           break;


        case (ABFC_SCSIP_ABORT | SCSI_PLUS_VALUE):
           /*****************************************/
           /* Send Abort                            */
           /*****************************************/

           WaitOtherIORB( npUCB );
           rc =  SendAbort(pGenioctl, EntryType);
           ReleaseOtherIORB( npUCB );

           break;

        default:
           /*****************************************/
           /* Invalid Function                      */
           /*****************************************/
           ;
     }
  }
  return( rc );

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  AllocDev                                                *
*                                                                           *
* DESCRIPTIVE NAME: Allocate Device                                         *
*                                                                           *
* FUNCTION:         This routine looks for the unit which fits to the       *
*                   request from Device-Class driver and issue Allocate     *
*                   command to it and returns the uniqur device handle to   *
*                   Device-Class Driver.                                    *
*                                                                           *
* ENTRY POINT:      AllocDev                                                *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near AllocDev(pGenioctl)

PRP_GENIOCTL pGenioctl;
{
  PSCSI_IN_ALLOCDEV    pParm = (PSCSI_IN_ALLOCDEV) (pGenioctl->ParmPacket);
  PSCSI_OUT_ALLOCDEV   pData = (PSCSI_OUT_ALLOCDEV)(pGenioctl->DataPacket);

  NPIORB               npIORB;

  NPUCB                npUCB         = UnitCB;

  USHORT               iUnit         = 0;
  USHORT               nth_available = 0;
  USHORT               nAvailable    = pParm->nAvailable;
  USHORT               UnitRemovable;
  USHORT               AllocRemovable;
  USHORT               Status;                                       /*@V74951*/
  USHORT               i;


  AllocRemovable = (pParm->fDevType & SCSI_REMOVABLE) ? 1 : 0;

  for ( ; iUnit < NumUnitCBs; iUnit++, npUCB++)
  {
     UnitRemovable  = (npUCB->UnitInfo.UnitFlags & UF_REMOVABLE) ? 1 : 0;

     if ((pParm->DevType == npUCB->UnitInfo.UnitType) &&
                                      (AllocRemovable == UnitRemovable) )
     {
        nth_available++;

        if (!(npUCB->IntUnitFlags & IUF_ALLOCATED))
        {
           if ((nAvailable == nth_available) || !nAvailable)         /*@V74951*/
           {
              WaitOtherIORB( npUCB );

              npIORB = &npUCB->Other_IORB;
              CLEAR_IORB_BUFF;

              npIORB->Length                = sizeof(IORB_UNIT_CONTROL);
              npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
              npIORB->CommandCode           = IOCC_UNIT_CONTROL;
              npIORB->CommandModifier       = IOCM_ALLOCATE_UNIT;
              npIORB->RequestControl        = IORB_ASYNC_POST;
              npIORB->NotifyAddress         = NotifyIORBDone;

              (*(npUCB->AdapterDriverEP))((PIORB) npIORB);

              DISABLE;
              while ( !(npIORB->Status & IORB_DONE) )
              {
                 DevHelp_ProcBlock ((ULONG)((PIORB) npIORB), -1L, 0);
                 DISABLE;
              }
              ENABLE;

              Status = npIORB->Status;                               /*@V74951*/

              ReleaseOtherIORB( npUCB );

              if (!(Status & IORB_ERROR))                            /*@V74951*/
              {
                 pData->hDev          = iUnit;
                 npUCB->IntUnitFlags |= IUF_ALLOCATED;
                 return( STDON );
              }
              else if ( nAvailable )                                 /*@V74951*/
              {                                                      /*@V74951*/
                break;                                               /*@V74951*/
              }                                                      /*@V74951*/
           }

        }
     }
  }

  return(STDON + STERR + SCSI_ERR_DEV_NOT_AVAILABLE);

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  ReadDevParms                                            *
*                                                                           *
* DESCRIPTIVE NAME: Read Device Parameters                                  *
*                                                                           *
* FUNCTION:         This routine returns device information for particular  *
*                   unit specified by device handle.  It issues  Unit Status*
*                   IORB to ADD to get some information like power-on/off.  *
*                                                                           *
* ENTRY POINT:      ReadDevParms                                            *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/


USHORT near ReadDevParms(pGenioctl)

PRP_GENIOCTL pGenioctl;
{
  PSCSI_OUT_READPARM   pData = (PSCSI_OUT_READPARM) pGenioctl->DataPacket;
  NPIORB               npIORB;
  NPIORB_UNIT_STATUS   npIORBUS;
  NPUCB                npUCB;

  USHORT               sUnit;
  USHORT               sFlag = 0;
  USHORT               i;

  npUCB    = &UnitCB[((PSCSI_IN)(pGenioctl->ParmPacket))->hDev];

  npIORB   =                      &npUCB->Other_IORB;
  npIORBUS = (NPIORB_UNIT_STATUS) npIORB;
  CLEAR_IORB_BUFF;

  npIORB->Length          = sizeof(IORB_UNIT_STATUS);
  npIORB->UnitHandle      = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode     = IOCC_UNIT_STATUS;
  npIORB->CommandModifier = IOCM_GET_UNIT_STATUS;
  npIORB->Timeout         = npUCB->Timeout;
  npIORB->RequestControl  = IORB_ASYNC_POST;
  npIORB->NotifyAddress   = NotifyIORBDone;

  npIORBUS->UnitStatus    = 0;

  (*(npUCB->AdapterDriverEP))((PIORB)npIORB);

  DISABLE;
  while ( !(npIORB->Status & IORB_DONE) )
  {
     DevHelp_ProcBlock ((ULONG)((PIORB) npIORB), -1L, 0);
     DISABLE;
  }
  ENABLE;

  sUnit = npUCB->UnitStatus = npIORBUS->UnitStatus;

  if (!(sUnit & US_POWER))
     sFlag |= DEVICE_POWER_OFF;

  if (sUnit & US_DEFECTIVE)
     sFlag |= DEVICE_DEFECTIVE;

  pData->indexDeviceKey  = 0;
  pData->levelSCBArcCard = 0;
  pData->indexAdapter    = npUCB->UnitInfo.AdapterIndex;
  pData->fDevice         = sFlag;
  pData->LUN             = npUCB->UnitInfo.UnitSCSILUN;
  pData->PUN             = npUCB->UnitInfo.UnitSCSITargetID;

  return(STDON);
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  Reset_Init                                              *
*                                                                           *
* DESCRIPTIVE NAME: Reset / Initialize                                      *
*                                                                           *
* FUNCTION:         This routine issues Reset IORB to ADD.                  *
*                                                                           *
* ENTRY POINT:      Reset_Init                                              *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near Reset_Init(pGenioctl)

PRP_GENIOCTL        pGenioctl;
{
  PSCSI_IN_RESET_INIT    pParm = (PSCSI_IN_RESET_INIT) (pGenioctl->ParmPacket);
  PSCSI_OUT              pData = (PSCSI_OUT)           (pGenioctl->DataPacket);

  NPUCB                  npUCB;
  NPIORB                 npIORB;
  NPSCSI_STATUS_BLOCK    npSSB;

  USHORT                  i;

  npUCB    = &UnitCB[((PSCSI_IN)(pGenioctl->ParmPacket))->hDev];

  npIORB = &npUCB->Other_IORB;
  CLEAR_IORB_BUFF;

  npSSB = &npUCB->Other_SSB;
  CLEAR_SSB_BUFF;

  npIORB->Length                = sizeof(IORB_DEVICE_CONTROL);
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = IOCC_DEVICE_CONTROL;
  npIORB->CommandModifier       = IOCM_RESET;
  npIORB->RequestControl       |= (IORB_ASYNC_POST | IORB_REQ_STATUSBLOCK);
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
  npIORB->pStatusBlock          = (NPBYTE)npSSB;
  npIORB->NotifyAddress         = NotifyIORBDone;                    /*@V61008*/
  *(NPUCB *)(npIORB->DMWorkSpace)  = npUCB;

                                            /* set sense data length & addr. */
  npSSB->ReqSenseLen = pParm->lnSenseData;
  npSSB->SenseData   = npUCB->pSenseData;

  (*(npUCB->AdapterDriverEP))((PIORB) npIORB);

  DISABLE;
  while ( !(npIORB->Status & IORB_DONE) )
  {
     DevHelp_ProcBlock ((ULONG)(PIORB) npIORB, -1L, 0);
     DISABLE;
  }
  ENABLE;

  return( CheckIORBError( npUCB, npIORB) );

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SetTimeout                                              *
*                                                                           *
* DESCRIPTIVE NAME: Set Timeout value                                       *
*                                                                           *
* FUNCTION:         This routine set the value of timeout into Unit CB.     *
*                                                                           *
* ENTRY POINT:      SetTimeout                                              *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/


USHORT near SetTimeout(pGenioctl)

PRP_GENIOCTL           pGenioctl;
{
  PSCSI_IN_SETTIMEOUT pParm = (PSCSI_IN_SETTIMEOUT) (pGenioctl->ParmPacket);
  USHORT              iUnit;

  iUnit = pParm->hDev;

  UnitCB[iUnit].Timeout = MS_to_S(pParm->vTimeout);

  return(STDON);

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  ReadTimeout                                             *
*                                                                           *
* DESCRIPTIVE NAME: Read Timeout Value                                      *
*                                                                           *
* FUNCTION:         This routine read the value of timeout from Unit CB.    *
*                   If the value is not set, return the default value.      *
*                                                                           *
* ENTRY POINT:      ReadTimeout                                             *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/


USHORT near ReadTimeout(pGenioctl)

PRP_GENIOCTL           pGenioctl;
{
  PSCSI_IN               pParm = (PSCSI_IN)              (pGenioctl->ParmPacket);
  PSCSI_OUT_READTIMEOUT  pData = (PSCSI_OUT_READTIMEOUT) (pGenioctl->DataPacket);

  USHORT                 iUnit = pParm->hDev;

  pData->vTimeout = S_to_MS(UnitCB[iUnit].Timeout);

  return(STDON);

}



/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  DeallocDev                                              *
*                                                                           *
* DESCRIPTIVE NAME: Deallocate Device                                       *
*                                                                           *
* FUNCTION:         This routine issues Deallocate Unit IORB to ADD.        *
*                                                                           *
* ENTRY POINT:      DeallocDev                                              *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near DeallocDev(pGenioctl)

PRP_GENIOCTL    pGenioctl;
{
  NPUCB                 npUCB;
  NPIORB                npIORB;
  USHORT                i;

  npUCB    = &UnitCB[((PSCSI_IN)(pGenioctl->ParmPacket))->hDev];

  npIORB = &npUCB->Other_IORB;
  CLEAR_IORB_BUFF;

  npIORB->Length                = sizeof(IORB_UNIT_CONTROL);
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = IOCC_UNIT_CONTROL;
  npIORB->CommandModifier       = IOCM_DEALLOCATE_UNIT;
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->RequestControl        = IORB_ASYNC_POST;
  npIORB->NotifyAddress         = NotifyIORBDone;

  (*(npUCB->AdapterDriverEP))((PIORB) npIORB);

  DISABLE;
  while ( !(npIORB->Status & IORB_DONE) )
  {
     DevHelp_ProcBlock ((ULONG)((PIORB) npIORB), -1L, 0);
     DISABLE;
  }
  ENABLE;

  if (!(npIORB->Status & IORB_ERROR))
  {
     npUCB->IntUnitFlags &= (~IUF_ALLOCATED);
  }
  return( CheckIORBError(npUCB, npIORB) );
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  RetTypeCnt                                              *
*                                                                           *
* DESCRIPTIVE NAME: Return Peripheral Type Count                            *
*                                                                           *
* FUNCTION:         This routine returns a count of the number of device of *
*                   a particular type which specified by Device-Class Driver*
*                                                                           *
* ENTRY POINT:      RetTypeCnt                                              *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near RetTypeCnt(pGenioctl)

PRP_GENIOCTL    pGenioctl;
{
  PSCSI_IN_RTNDEVCNT    pParm =(PSCSI_IN_RTNDEVCNT) (pGenioctl->ParmPacket);
  PSCSI_OUT_RTNDEVCNT   pData =(PSCSI_OUT_RTNDEVCNT)(pGenioctl->DataPacket);

  NPUCB         npUCB = UnitCB;

  USHORT        iUnit  = 0;
  USHORT        cntDev = 0;
  USHORT        DeviceType;
  USHORT        UnitRemovable;
  USHORT        FindRemovable;

  DeviceType    =  (USHORT)(pParm->DevType);
  FindRemovable =  (pParm->fDevType & SCSI_REMOVABLE) ? 1 : 0;

  for ( ; iUnit < NumUnitCBs; iUnit++, npUCB++)
  {
     UnitRemovable = (npUCB->UnitInfo.UnitFlags & UF_REMOVABLE)   ? 1 : 0;

     if ((npUCB->UnitInfo.UnitType == DeviceType) &&
                                      (FindRemovable == UnitRemovable) )
     {
        cntDev++;
     }
  }

  pData->numDev = cntDev;
  return(STDON);

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SendAbort                                               *
*                                                                           *
* DESCRIPTIVE NAME: Send Abort                                              *
*                                                                           *
* FUNCTION:         This routine issues to ABORT IORB to ADD.               *
*                                                                           *
* ENTRY POINT:      SendAbort                                               *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pParm         Pointer to parameter packet               *
*                   pData         Pointer to data packet                    *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near SendAbort(pGenioctl, EntryType)

PRP_GENIOCTL           pGenioctl;
USHORT                 EntryType;
{
  PSCSI_IN_SENDABORT   pParm = (PSCSI_IN_SENDABORT)(pGenioctl->ParmPacket);
  PSCSI_OUT            pData = (PSCSI_OUT)         (pGenioctl->DataPacket);

  NPIORB               npIORB;
  NPSCSI_STATUS_BLOCK  npSSB;
  NPUCB                npUCB = &UnitCB[pParm->hDev];

  USHORT               i;

  if ( ValidateUserPacket(pGenioctl, npUCB, EntryType) )             /*@V55360*/
  {                                                                  /*@V55360*/
                                            /* get IORB buffer address */
    npIORB = &npUCB->Other_IORB;
    CLEAR_IORB_BUFF;
                                            /* get SSB buffer address  */
    npSSB = &npUCB->Other_SSB;
    CLEAR_SSB_BUFF;

    npIORB->Length                = sizeof(IORB_DEVICE_CONTROL);
    npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;

    npIORB->CommandCode           = IOCC_DEVICE_CONTROL;
    npIORB->CommandModifier       = IOCM_ABORT;
    npIORB->RequestControl        = IORB_REQ_STATUSBLOCK | IORB_ASYNC_POST;

    npIORB->Timeout               = npUCB->Timeout;

    npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
    npIORB->pStatusBlock          = (NPBYTE)npSSB;

    npIORB->NotifyAddress         = NotifyIORBDone;

                                             /* set sense data length & addr. */
    npSSB->ReqSenseLen = pParm->lnSenseData;
    npSSB->SenseData   = npUCB->pSenseData;

    (*(npUCB->AdapterDriverEP))((PIORB)npIORB);

    DISABLE;
    while ( !(npIORB->Status & IORB_DONE) )
    {
       DevHelp_ProcBlock ((ULONG)((PIORB) npIORB), -1L, 0);
       DISABLE;
    }
    ENABLE;

    npUCB->LastStatus    = npIORB->Status;
    npUCB->LastErrCode   = npIORB->ErrorCode;
    npUCB->LastSSB       = *(NPSCSI_STATUS_BLOCK) npIORB->pStatusBlock;
    npUCB->ppLastSCB     = 0;

    return( CheckIORBError(npUCB, npIORB) );
  }                                                                  /*@V55360*/
  else                                                               /*@V55360*/
  {                                                                  /*@V55360*/
    return( STDON + STERR + ERROR_I24_INVALID_PARAMETER );           /*@V55360*/
  }                                                                  /*@V55360*/
}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  NotifyIORBDone                                          *
*                                                                           *
* DESCRIPTIVE NAME: IORB Notification routine                               *
*                                                                           *
* FUNCTION:         Does a DevHelp_ProcRun on the completed IORB address.   *
*                   This routine is used by sections of the OS2SCSI that    *
*                   need an in-line wait for an IORB completion.            *
*                                                                           *
* ENTRY POINT:                                                              *
*                                                                           *
* LINKAGE:          Call FAR                                                *
*                                                                           *
* INPUT:            pIORB                                                   *
*                                                                           *
* EXIT-NORMAL:                                                              *
*                                                                           *
* EXIT-ERROR:                                                               *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

void _loadds far
NotifyIORBDone(pIORB)

PIORBH           pIORB;
{
  USHORT         AwakeCount;

  DevHelp_ProcRun((ULONG)(pIORB), &AwakeCount);

  return;

}
