/*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 = "@(#)tcfgscsi.c  6.1 92/02/13";*/

/*********************************************************************
 *
 *
 *
 *
 *********************************************************************/

/*** TCFGSCSI.C - SCSI Inquiry routine for TESTCFG device driver
 *   DESCRIPTION
 *
 *     This file contains the routines for issuing SCSI inquiry
 *     commands.
 *
 *
*/
#include "testcfg.h"
#include "IORB.h"
#include "SCSI.h"
#include "devclass.h"
#include "tcfgscsi.h"
#include "tcfgx.h"
#define RMCode SWAPCODE
#include "rmcalls.h"
#include "tcfgex.h"



/*** CFG_SCSI_Inquiry -  Issue SCSI Inquiry for specified device type
 *
 *   This routine is used to issue SCSI INQUIRY command a specified
 *   device type
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 */
USHORT far CFG_SCSI_Inquiry(PREQPACKET rp)
{
 PINQUIRYPARMPACKET  pParm;
 PINQUIRYDATAPACKET  pBuffer;

  /* Get and Verify Command Information Field */
  pParm = (PINQUIRYPARMPACKET)rp->s.IOCtl.parameters;
  if(VerifyAccess(pParm,sizeof(PINQUIRYPARMPACKET),VERIFY_READACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  if(pParm->CmdWord==0) {

    /* Get and Verify Buffer Field */
    pBuffer = (PINQUIRYDATAPACKET)rp->s.IOCtl.buffer;
    if(VerifyAccess(pBuffer,sizeof(INQUIRYDATAPACKET),VERIFY_READWRITEACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

    Build_UnitCBs(pBuffer,pParm->Dev_Type);
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}



/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  Build_UnitCBs                                           *
*                                                                           *
*                                                                           *
* ENTRY POINT:      Build_UnitCBs                                           *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:                                                                    *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       -1                                                      *
*                                                                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

unsigned near Build_UnitCBs(PINQUIRYDATAPACKET  pBuffer, UCHAR DevType)

{

  PIORB_CONFIGURATION    pIORBDT;        // ptr to IORB

  PADAPTERINFO         pAdapterInfo;      // ptr to AdapterInfo
  PDEVICETABLE         pDeviceTable;      // ptr to Device table
  PUNITINFO            pUnitInfo;         // ptr to UnitInfo


  USHORT                rc;
  USHORT                i,j,k;
  USHORT                NumDrivers;
  USHORT                DevBus;

  /*--------------------------------------------------------------------*/
  /* Get the adapter device tables for each adapter driver and          */
  /* issue SCSI INQUIRY command to each unit                            */
  /*--------------------------------------------------------------------*/

  pBuffer->nDevices = 0;
  pDeviceTable = (PDEVICETABLE)&DeviceTableBuffer;

  rc = GetDCTable((POINTER) &pDriverTable);

  if (!rc)
     {
     /*----------------------------------------------------*/
     /* For each .ADD registered in the DEVICE CLASS TABLE */
     /*----------------------------------------------------*/

     NumDrivers = pDriverTable->DCCount;
     for (i= 0; i < NumDrivers; i++)
        {
        pIORBDT   = (PIORB_CONFIGURATION)&ConfigIORB;

        pIORBDT->iorbh.Length          = sizeof(IORB_CONFIGURATION);
        pIORBDT->iorbh.CommandCode     = IOCC_CONFIGURATION;
        pIORBDT->iorbh.CommandModifier = IOCM_GET_DEVICE_TABLE;
        pIORBDT->iorbh.Status          = 0;
        pIORBDT->iorbh.ErrorCode       = 0;
        pIORBDT->iorbh.RequestControl  = IORB_ASYNC_POST;
        pIORBDT->iorbh.NotifyAddress   = &IORBPost;
        pIORBDT->pDeviceTable          = pDeviceTable;
        pIORBDT->DeviceTableLen        = MAX_DT_SIZE;

        OFFSETOF(DriverEP)   = pDriverTable->DCTableEntries[i].DCOffset;
        SELECTOROF(DriverEP) = pDriverTable->DCTableEntries[i].DCSelector;

        ZeroCB((PBYTE)pDeviceTable, MAX_DT_SIZE);

        (*DriverEP) ((FARPOINTER)(pIORBDT));


        PUSHFLAGS;
        DISABLE;
        while ( !( pIORBDT->iorbh.Status & IORB_DONE ) )
           {
           ProcBlock ((ULONG)pIORBDT, -1L, 1);
           DISABLE;                          /* Block does an enable  */
           }
        ENABLE;
        POPFLAGS;

        if (!(pIORBDT->iorbh.Status & IORB_ERROR))
           {
           for (j = 0; j < pDeviceTable->TotalAdapters; j++)
              {
              pAdapterInfo =  pDeviceTable->pAdapter[j];
              DevBus = (pAdapterInfo->AdapterDevBus & 0x00ff);


              if (( DevBus == AI_DEVBUS_SCSI_1) ||
                  ( DevBus == AI_DEVBUS_SCSI_2) ||
                  ( DevBus == AI_DEVBUS_SCSI_3) ||
                  ( DevBus == AI_DEVBUS_NONSCSI_CDROM) ||
                  ((DevBus == AI_DEVBUS_ST506) && (DevType == UIB_TYPE_CDROM)) )
                 {

                 for (k = 0; k < pAdapterInfo->AdapterUnits; k++)
                    {

                    /* Inquiry only types devices requested               */
                    /* that are not "defective"                           */

                    if ((pAdapterInfo->UnitInfo[k].UnitType == DevType) &&
                      !(pAdapterInfo->UnitInfo[k].UnitFlags & UF_DEFECTIVE) )
                       {
                       pUnitInfo = &(pAdapterInfo->UnitInfo[k]);
                       /* Do inquiry  */
                       rc = Do_Inquiry(pUnitInfo, pBuffer, DevBus);

                       /* If Error (might not be allocated       */
                       /* but we can't depend on the error code) */
                       if (rc & IORB_ERROR)
                          {
                          /* then allocate it and dos inquiry */
                          rc = Do_AllocDeallocUnit(IOCM_ALLOCATE_UNIT,
                                                   pUnitInfo);
                          /* Skip if allocate fails           */
                          if (!(rc & IORB_ERROR))
                             {
                             rc = Do_Inquiry(pUnitInfo, pBuffer, DevBus);
                             rc = Do_AllocDeallocUnit(IOCM_DEALLOCATE_UNIT,
                                                      pUnitInfo);
                             } /* endif */
                          }
                       }
                    }  /* end unit loop */
                 } /* endif */
              }  /* end adapter loop */
           }
        }  // end driver loop

     return(0);
     }
  else
     {
     return(-1);
     }
}

/*---------------------------------------------------------------------------
;
;** BuildCDB_Inquiry - Setup SCSI Inquiry Command Descriptor Block
;
;   This routine builds a SCSI Inquiry CDB.
;
;   BuildCDB_Inquiry (PUNITINFO pUnitInfo, PIORB_CDB pIORB)
;
;   ENTRY:   pUnitInfo       - pointer to UnitInfo
;            pIORB           - IORB
;
;   RETURN:
;
----------------------------------------------------------------------------*/
void near BuildCDB_Inquiry(pUnitInfo, pIORB, DevBus)

PUNITINFO    pUnitInfo;
PIORB_CDB  pIORB;                             /* ptr to IORB              */
USHORT     DevBus;
{

  struct
  CDB_Inquiry *pCDB;                           /* ptr to CDB               */


  /* Build a CDB passthru IORB */

  BuildIORB_PassthruCDB (pUnitInfo,
               sizeof(INQUIRYDATA),
               (ULONG) (ppDataSeg+(ULONG)((USHORT)&(InquiryData))),
               (PIORB_CDB) pIORB );

  if (DevBus == AI_DEVBUS_ST506)
  {
     pIORB->apt.ControllerCmdLen = ATAPI_Cmd_Length;
  }
  else
     pIORB->apt.ControllerCmdLen = sizeof(struct CDB_Inquiry);

  pIORB->apt.Flags |= PT_DIRECTION_IN;

  /* Setup the CDB */

  pCDB = (struct CDB_Inquiry *) &pIORB->CDB;
  pCDB->OpCode = SCSI_INQUIRY;
  pCDB->LUN = pUnitInfo->UnitSCSILUN;
  pCDB->alloc_length = sizeof(INQUIRYDATA);

}

/*---------------------------------------------------------------------------
;
;** BuildIORB_PassthruCDB - Setup a passthru IORB for a CDB
;
;   This routine allocates and sets up a CDB passthru IORB
;
;   BuildIORB_PassthruCDB (PUNITINFO pUnitInfo, ULONG data_length,
;                          ULONG ppData, PIORB_CDB pIORB)
;
;   ENTRY:   pUnitInfo       - pointer to UnitInfo
;            data_length     - length of data to transfer
;            ppData          - physical address of data buffer
;            pIORB           - pointer to IORB
;
;   RETURN:
;
----------------------------------------------------------------------------*/

void near BuildIORB_PassthruCDB (pUnitInfo, data_length, ppData, pIORB)

PUNITINFO       pUnitInfo;
ULONG           data_length;
ULONG           ppData;
PIORB_CDB       pIORB;

{
   PIORB_DMWORK pDMWork;



   pDMWork = (PIORB_DMWORK) &(pIORB->apt.iorbh.DMWorkSpace);

    pIORB->apt.iorbh.UnitHandle = pUnitInfo->UnitHandle;
    pIORB->apt.iorbh.NotifyAddress = &IORBPost;

   /* If data transfer involved, setup the scatter/gather list     */

   if (data_length != 0)
   {
      pIORB->apt.cSGList = 1;
      pIORB->apt.pSGList = (FARPOINTER) pIORB;
      OFFSETOF(pIORB->apt.pSGList) = (USHORT) ( &(pDMWork->SGList) );
      pIORB->apt.ppSGLIST =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pDMWork->SGList)) );

      if (ppData == -1)
         pDMWork->SGList.ppXferBuf =
                      (ULONG)(ppDataSeg+(ULONG)((USHORT)&(pIORB->CDB_data)));
      else
         pDMWork->SGList.ppXferBuf = ppData;

      pDMWork->SGList.XferBufLen = data_length;
   }

   /* Fill in the pointer to the SCSI CDB */

   pIORB->apt.pControllerCmd = (FARPOINTER) pIORB;
   OFFSETOF(pIORB->apt.pControllerCmd) = (USHORT) ( &(pIORB->CDB) );
   pIORB->apt.ppSCB =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pIORB->CDB)) );
   ZeroCB((PBYTE)pIORB->apt.iorbh.ADDWorkSpace, ADD_WORKSPACE_SIZE);


}

/*---------------------------------------------------------------------------
;
;** Do_Inquiry  - Do the actual inquiry for one unit
;
;   This routine does the acutal inquiry for one unit.
;
;   Do_Inquiry (PUNITINFO pUnitInfo, PINQUIRYDATAPACKET  pBuffer);
;
;   ENTRY:   pUnitInfo       - pointer to UnitInfo
;            pBuffer         - pointer to user buffer
;
;   RETURN NORMAL : 0
;
;   RETURN: IORB.Status
;
----------------------------------------------------------------------------*/

USHORT near Do_Inquiry(pUnitInfo,pBuffer,DevBus)
  PUNITINFO            pUnitInfo;         // ptr to UnitInfo
  PINQUIRYDATAPACKET  pBuffer;
  USHORT              DevBus;

{
  PIORB_CDB              pIORBPT;        // ptr to IORB


  pIORBPT = &PassthruIORB;

  pIORBPT->apt.iorbh.Length  = sizeof(IORB_ADAPTER_PASSTHRU);
  pIORBPT->apt.iorbh.CommandCode    = IOCC_ADAPTER_PASSTHRU;
  pIORBPT->apt.iorbh.CommandModifier= IOCM_EXECUTE_CDB;
  pIORBPT->apt.iorbh.Status         = 0;
  pIORBPT->apt.iorbh.ErrorCode      = 0;
  pIORBPT->apt.iorbh.RequestControl = IORB_ASYNC_POST;
  pIORBPT->apt.iorbh.NotifyAddress  = &IORBPost;

  BuildCDB_Inquiry(pUnitInfo, (PIORB_CDB) pIORBPT, DevBus);

  ZeroCB((PBYTE)&InquiryData,
           (USHORT) sizeof(INQUIRYDATA));

  FilterADDHandle = pUnitInfo->FilterADDHandle;

  if ( !FilterADDHandle )
     {
     (*DriverEP)((FARPOINTER)(pIORBPT));
     }
  else
     {
     OFFSETOF(DriverEPF) =
       pDriverTable->DCTableEntries[FilterADDHandle-1].DCOffset;
     SELECTOROF(DriverEPF) =
       pDriverTable->DCTableEntries[FilterADDHandle-1].DCSelector;

     (*DriverEPF)((FARPOINTER)(pIORBPT));
     }


  PUSHFLAGS;
  DISABLE;
  while ( !( pIORBPT->apt.iorbh.Status & IORB_DONE ) )
     {
     ProcBlock ((ULONG)pIORBPT, -1L, 1);
     DISABLE;                 /* Block does an enable  */
     }
  ENABLE;
  POPFLAGS;

  if (!( pIORBPT->apt.iorbh.Status & IORB_ERROR))
     {
     /* Copy into buffer then increment nDevices */

     BlockCopy((PBYTE)&(pBuffer->InquiryEntry[pBuffer->nDevices]),
                 (PBYTE)&InquiryData,
                 (USHORT) sizeof(INQUIRYDATA)) ;
     pBuffer->nDevices++;
     }
 return(pIORBPT->apt.iorbh.Status);
}

/*---------------------------------------------------------------------------
;
;** Do_AllocDeallocUnit - Allocate or Deallocate a unit
;
;   This routine does allocates one unit or Deallocates one unit
;
;   Do_AllocDeallocUnit (USHORT command_modifier,PUNITINFO pUnitInfo)
;
;   ENTRY:   command_modifier - either ALLOCATE or DEALLOCATE IORB command
;            pUnitInfo        - pointer to UnitInfo
;
;   RETURN: IORB.Status
;
----------------------------------------------------------------------------*/

USHORT near Do_AllocDeallocUnit(command_modifier, pUnitInfo)
  USHORT     command_modifier;
  PUNITINFO            pUnitInfo;         // ptr to UnitInfo

{
   PIORB_UNIT_CONTROL pIORB;

   pIORB = (NPIORB_UNIT_CONTROL)&UnitControlIORB;

   BuildIORB_UnitControl (command_modifier,
                          pUnitInfo->UnitHandle);

   FilterADDHandle = pUnitInfo->FilterADDHandle;

   if ( !FilterADDHandle )
      {
      (*DriverEP) ((FARPOINTER)(pIORB));
      }
   else
      {
      OFFSETOF(DriverEPF) =
           pDriverTable->DCTableEntries[FilterADDHandle-1].DCOffset;
      SELECTOROF(DriverEPF) =
           pDriverTable->DCTableEntries[FilterADDHandle-1].DCSelector;

      (*DriverEPF) ((FARPOINTER)(pIORB));
      }

  PUSHFLAGS;
  DISABLE;
  while ( !( pIORB->iorbh.Status & IORB_DONE ) )
     {
     ProcBlock ((ULONG)pIORB, -1L, 1);
     DISABLE;                 /* Block does an enable  */
     }
  ENABLE;
  POPFLAGS;


   return(pIORB->iorbh.Status);
}


/*---------------------------------------------------------------------------
;
;** BuildIORB_UnitControl - Build a Unit Control IORB
;
;   This routine build a unit control IORB.
;
;   VOID Build_UnitControl(USHORT command_modifier, USHORT unit_handle);
;
;   ENTRY:   command_modifier   - IORB command modifier code
;            unit_handle        - unit handle
;
;
;   RETURN:
;
;   NOTES:
;
;---------------------------------------------------------------------------*/
void near BuildIORB_UnitControl (command_modifier, unit_handle)

USHORT     command_modifier;
USHORT     unit_handle;

{
   NPIORB_UNIT_CONTROL pIORB;

   pIORB = (NPIORB_UNIT_CONTROL)&UnitControlIORB;

   pIORB->iorbh.Length = sizeof(IORB_UNIT_CONTROL);
   pIORB->iorbh.CommandCode = IOCC_UNIT_CONTROL;
   pIORB->iorbh.CommandModifier = command_modifier;
   pIORB->iorbh.Status = 0;
   pIORB->iorbh.ErrorCode = 0;
   pIORB->iorbh.UnitHandle = unit_handle;
   pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
   pIORB->iorbh.NotifyAddress = &IORBPost;
   pIORB->Flags = 0;

   pIORB->pUnitInfo = 0;
   pIORB->UnitInfoLen = 0;
   ZeroCB((PBYTE)pIORB->iorbh.ADDWorkSpace, ADD_WORKSPACE_SIZE);
}


/* Dummy notification callout for ADDs */

void _loadds far IORBPost(PIORBH pIORB)
{
   USHORT   AwakeCount;

   ProcRun((ULONG)pIORB, &AwakeCount);
   return;

}

