/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/* SCCSID = %W% %E% */
/**************************************************************************
 *
 * SOURCE FILE NAME = ASPIINIT.C
 *
 * DESCRIPTIVE NAME = OS2ASPI.DMD - OS/2 ASPI Device Manager
 *                    ASPI Device Manager - Initialization
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  Builds configuration dependent data in response to OS/2
 *              KERNEL initialization request.
 *
 *
*/

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

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

#define INCL_INITRP_ONLY
#include <reqpkt.h>

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

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

#include <cmdparse.h>
#include <cmdpdefs.h>

extern      USHORT               NumADDs;                /* number of adapter drivers */
extern      UCHAR                numberOfASPIAdapters;
extern      UCHAR                numberOfASPITargets;
extern      USHORT               freeSRBSpace;           /* bytes available for SRBs  */
extern      NPSRB_LINK           npSRBLinkFreeList;      /* pointer to available SRB  */
extern      PVOID                pDataSeg;               /* Our data segment pointer  */
extern      ULONG                ppDataSeg;              /* Our data segment pointer  */
extern      UCHAR                Pool[];
extern      IORBH                InitIORB;
extern      UCHAR                deviceTableBuffer[MAX_DT_SIZE];
extern      DEVICETABLE          *pDeviceTable;
extern      NPACB                npLastACB;
extern      NPACB                npACBAnchor;
extern      NPATE                npLastATE;
extern      NPATE                npFirstATE;
extern      NPIORB_UNIT_CONTROL  npIORBUnitControl;
extern      USHORT               allocationOverride;
extern      USHORT               shareTargets;
extern      OPTIONTABLE          opttable;
extern      UCHAR                *invalidParameters;
extern      USHORT               outbuf_len;
extern      PBYTE                poutbuf;
extern      MSGTABLE             InitMsg;
extern      UCHAR                ASPI_PDDName[];
extern      DIREXCEPT            DirTableExceptions[];
extern      USHORT               MaxDirExceptions;
extern      NPIORB_ADAPTER_PASSTHRU npIORBInq;

extern      USHORT SendIORBandWait( npIORB, pADD_IORB_Entry );

/*--------------------------------------------------*/
/*                                                  */
/* Process Base Initialization Request Packet       */
/* ------------------------------------------       */
/*                                                  */
/*                                                  */
/*--------------------------------------------------*/

/*********************************************************
*                                                        *
*   Procedure Name : ASPIInit                            *
*                                                        *
*   Description : This procedure initializes the ASPI    *
*   device manager. All available SCSI devices will      *
*   be allocated data structures to record their         *
*   pertinent information.                               *
*                                                        *
*   Input :                                              *
*         pRPI - A pointer to the initialization         *
*            request packet.                             *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
USHORT ASPIInit(PRPINITIN pRPI)

   {
   USHORT             rc;
   USHORT             i;
   struct DevClassTableStruc far *pDevClassTable;  /* ptr to registered ADD EPs */
   struct DevClassTableEntry far *pTableADDEntry;
   struct DevClassTableEntry far *pCurrentADDEntry;
   VOID (FAR * pADD_IORB_Entry) ();                /* Driver entry point        */
   PRPINITOUT   pRPO;
   PDDD_PARM_LIST pDDD_Parm_List;

   InitComplete = 1;
   pRPO = (PRPINITOUT) pRPI;
   Device_Help = pRPI->DevHlpEP;
   pDDD_Parm_List = (PDDD_PARM_LIST) pRPI->InitArgs;

   rc = DevHelp_GetDOSVar((USHORT) DHGETDOSV_DEVICECLASSTABLE,
                           1,
                          (PPVOID) &pDevClassTable);

   if (ParseCmdLine(pDDD_Parm_List))
      {
      TTYWrite(invalidParameters);
      }

   /* Save the physical address of our data segment */
   pDataSeg = (PVOID) &pDataSeg;                                     /*@V53752*/
   OFFSETOF(pDataSeg) = 0;                                           /*@V53752*/
                                                                     /*@V53752*/
   rc = DevHelp_VirtToPhys(pDataSeg,                                 /*@V53752*/
                           (PULONG) &ppDataSeg);                     /*@V53752*/

   if (!rc)
      {
      NumADDs   = pDevClassTable->DCCount;
      pTableADDEntry = pCurrentADDEntry = pDevClassTable->DCTableEntries;

      /* Scan all ADD device tables for available devices */
      for (i = 0; i < NumADDs; i++, pCurrentADDEntry++ )
         {
         OFFSETOF(pADD_IORB_Entry) = pCurrentADDEntry->DCOffset;
         SELECTOROF(pADD_IORB_Entry) = pCurrentADDEntry->DCSelector;

         /* Get the device table for the current ADD */
         if ( GetADDDeviceTable(pADD_IORB_Entry))
            continue;

         /* Search the current table for SCSI devices */
         ScanForSCSIAdapters(pADD_IORB_Entry,pTableADDEntry);
         }
      }

   /* Abort "quietly" because there are no available targets */
   if (numberOfASPITargets == 0)
      {
      pRPO->Unit   = 0;
      pRPO->CodeEnd = 0;
      pRPO->DataEnd = 0;
      return(0x8115);
      }
   else
      {
      /* Allocate space for all possible SRBs */
      InitSRBPool();

      /* Register the entry point for VASPI.SYS */
      DevHelp_RegisterPDD(ASPI_PDDName,&VirtualASPIEntry);

      /* All devices were successfully initialized. Return no errors */
      /* and throw away any unnecessary code.                        */

      pRPO->Unit   = 0;
      pRPO->CodeEnd = (USHORT) &ASPIInit;
      pRPO->DataEnd = (USHORT) npConfigPool;

      return (STDON);
      }
   }


/*********************************************************
*                                                        *
*   Procedure Name : GetADDDeviceTable                   *
*                                                        *
*   Description : This procedure retrieves the table     *
*   of devices controlled by the current ADD.            *
*                                                        *
*   Input :                                              *
*         pADD_IORB_Entry - A pointer to the ADD entry   *
*            point.                                      *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
USHORT GetADDDeviceTable(VOID (FAR * pADD_IORB_Entry) (PIORB))

   {
   NPIORB_CONFIGURATION pIORB = (NPIORB_CONFIGURATION) &InitIORB;

   /* Build an IORB and send it to the ADD */
   pIORB->iorbh.Length          = sizeof(IORB_CONFIGURATION);
   pIORB->iorbh.CommandCode     = IOCC_CONFIGURATION;
   pIORB->iorbh.CommandModifier = IOCM_GET_DEVICE_TABLE;
   pIORB->iorbh.Status          = 0;
   pIORB->iorbh.ErrorCode       = 0;
   pIORB->pDeviceTable          = pDeviceTable;
   pIORB->DeviceTableLen        = sizeof(deviceTableBuffer);
   pIORB->iorbh.NotifyAddress = &InitPost;

   return(SendIORBandWait((NPIORB) pIORB, pADD_IORB_Entry ));
   }


//
// Proceedure Name: AdapterIsValid
//
// Verify the input adapter is valid by confirming the adapter
// is a SCSI adapter and that the adapter's name is not in the
// list of unsupported adapter names.  The adapters in this list
// are known to never contain ASPI devices.  Such as most RAID
// devices.
//
BOOL NEAR AdapterIsValid( NPADAPTERINFO  npAI )
{
   USHORT  i;
   USHORT  n;
   UCHAR   devBus;           // Type of bus used by current adapter

   devBus = (UCHAR)npAI->AdapterDevBus;

   if( devBus == AI_DEVBUS_SCSI_1 ||
       devBus == AI_DEVBUS_SCSI_2 ||
       devBus == AI_DEVBUS_SCSI_3 )
   {
      for( i = 0; i < nUnsupportedAdapters; i++ )
      {
         n = strlength( pUnsupportedAdapterNames[i] );
         if( strncmp( (CHAR FAR*)(npAI->AdapterName), pUnsupportedAdapterNames[i], n ) )
         {
            // Found a match, so no ASPI devices on this adapter.
            return FALSE;
         }
      }

      // This is a SCSI adapter and the it's name is not on the unsupported list.
      return TRUE;
   }
   else
   {
      // Not a SCSI adapter, so no ASPI devices on this Adapter.
      return FALSE;
   }
}

/*********************************************************
*                                                        *
*   Procedure Name : ScanforSCSIAdapters                 *
*                                                        *
*   Description : This procedure verifies the current    *
*   ADD controls SCSI devices and records all the        *
*   information about available devices.                 *
*                                                        *
*   Input :                                              *
*         pADD_IORB_Entry - A pointer to the ADD entry   *
*            point, assuming there is no filter.         *
*         pADDEntryTable - A pointer to all the entry    *
*            points for units attached to the ADD.       *
*                                                        *
*   Output :   This function returns zero if at least    *
*   one available target is found in the current ADD.    *
*   A non-zero value indicates some error was detected.  *
*                                                        *
*********************************************************/
USHORT SCANforSCSIAdapters(VOID (FAR * pADD_IORB_Entry) (PIORB),
                           struct DevClassTableEntry far *pADDEntryTable)
   {
   NPADAPTERINFO  npAI;
   NPUNITINFO     npUI;

   USHORT         NumAdapters;      /* Number of adapters using current ADD */
   USHORT         NumUnits;         /* Number of devices on current adapter */
   USHORT         filterADDHandle;

   USHORT         i, j;             /* Loop control variables               */
   USHORT         resultCode = -1;  /* Assume no targets will be found      */


   NPACB          npACB;            /* Pointer to adapter control block created */
   USHORT         allocationStatus; /* Result of allocating the current target */
   VOID (FAR * pDevice_ADD_Entry) (PIORB);

   NumAdapters = pDeviceTable->TotalAdapters;

   /* Scan all adapters using the current ADD */
   for (i = 0; i < NumAdapters; i++ )
      {
      npAI =  pDeviceTable->pAdapter[i];

      
      if( AdapterIsValid( npAI ) )                                     
         {
         NumUnits  = npAI->AdapterUnits;
         npUI      = npAI->UnitInfo;

         /* Create an ACB for the current adapter */
         if ((npACB = BuildACB(npAI)) != 0)
            {
            /* Reset last ATE pointer because no ATE have been created.*//*@V55762*/
            npLastATE = 0;                                              //*@V55762*/

            /* Check all devices attached to the current adapter */
            for (j = 0; j < NumUnits; j++, npUI++)
               {

               /* If this target has a filter ADD, use that entry point */
               filterADDHandle = npUI->FilterADDHandle;
               if (filterADDHandle)
                  {
                  OFFSETOF(pDevice_ADD_Entry) =
                           pADDEntryTable[filterADDHandle - 1].DCOffset;

                  SELECTOROF(pDevice_ADD_Entry) =
                           pADDEntryTable[filterADDHandle - 1].DCSelector;
                  }
               else
                  pDevice_ADD_Entry = pADD_IORB_Entry;

               /* Skip all devices already allocated unless the user has */
               /* requested allocation override */
               allocationStatus = CheckUnitAllocated(pDevice_ADD_Entry,npUI);

               if ((allocationStatus) && (!allocationOverride))
                  continue;

               /* Build an ATE for each available device */
               if (BuildATE(pDevice_ADD_Entry,
                            npUI,
                            allocationStatus) == 0)
                  {
                  resultCode = -1;      /* Target entry structure NOT built */
                  break;
                  }

               /* Remember at least one target was found */
               resultCode = 0;
               }
            }
          }
      }

   return(resultCode);
   }

/*********************************************************
*                                                        *
*   Procedure Name : CheckUnitAllocated                  *
*                                                        *
*   Description : This procedure confirms that the       *
*   current unit is NOT allocated to another device      *
*   manager.                                             *
*                                                        *
*   Input :                                              *
*         pADD_IORB_Entry - A pointer to the ADD entry   *
*            point.                                      *
*         npUI - A pointer to the information on the     *
*            current device.                             *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
USHORT CheckUnitAllocated( VOID (FAR * pADD_IORB_Entry) (PIORB),NPUNITINFO npUI )

   {
   USHORT resultCode;

   /* Clean up the IORB before filling in appropriate fields */

   memset((PBYTE) npIORBUnitControl,
          0,
          sizeof(IORB_UNIT_CONTROL));

   /* Set up IORB to check unit allocation */

   npIORBUnitControl->iorbh.Length          = sizeof(IORB_UNIT_CONTROL);
   npIORBUnitControl->iorbh.UnitHandle      = npUI->UnitHandle;
   npIORBUnitControl->iorbh.CommandCode     = IOCC_UNIT_CONTROL;
   npIORBUnitControl->iorbh.CommandModifier = IOCM_ALLOCATE_UNIT;
   npIORBUnitControl->iorbh.Status          = 0;
   npIORBUnitControl->iorbh.ErrorCode       = 0;
   npIORBUnitControl->Flags                 = 0;
   npIORBUnitControl->iorbh.NotifyAddress   = &InitPost;

   resultCode = SendIORBandWait((NPIORB) npIORBUnitControl,
                                pADD_IORB_Entry);

   /* If the unit was unallocated, release it before returning */

   if (!resultCode)
      {
      npIORBUnitControl->iorbh.UnitHandle      = npUI->UnitHandle;
      npIORBUnitControl->iorbh.CommandModifier = IOCM_DEALLOCATE_UNIT;
      npIORBUnitControl->iorbh.Status          = 0;
      npIORBUnitControl->iorbh.ErrorCode       = 0;
      npIORBUnitControl->Flags                 = 0;
      npIORBUnitControl->iorbh.NotifyAddress   = &InitPost;
      resultCode = SendIORBandWait((NPIORB) npIORBUnitControl,
                                   pADD_IORB_Entry );
      }

   return(resultCode);
   }

/*********************************************************
*                                                        *
*   Procedure Name : BuildACB                            *
*                                                        *
*   Description : This procedure creates an adapter      *
*   control block for the current adapter                *
*                                                        *
*   Input :                                              *
*         npAI - A pointer to the information on the     *
*            current adapter.                            *
*                                                        *
*   Output :   A pointer to the adapter control block    *
*                                                        *
*********************************************************/
NPACB BuildACB(NPADAPTERINFO npAI )

   {
   NPACB npACB;

   /* Allocate memory for the data structure */
   if ((npACB = (NPACB) InitAllocate(sizeof(ACB))) != 0)
      {
      /* If this is the first adapter, make it the anchor */
      if (!npACBAnchor)
         npACBAnchor = npACB;
      else
         npLastACB->npNextACB = npACB;

      /* Initialize all fields of the ACB */
      npLastACB = npACB;
      npACB->npNextACB        = 0;
      npACB->npFirstATE       = 0;
      npACB->AdapterFlags     = npAI->AdapterFlags;
      npACB->AdapterIndex     = numberOfASPIAdapters++;
      npACB->AdapterTargetID  = npAI->AdapterSCSITargetID;
      memcpy(npACB->AdapterName,
             npAI->AdapterName,
             sizeof(npACB->AdapterName));

      /* Assume the adapter supports residual byte count */           /*@V53040*/
      /* and scatter/gather                              */           /*@V53040*/
      npACB->AdapterFeatures     = ASPI_RESIDUAL_BYTE                 /*@V53040*/
                                                | ASPI_SCATTER_GATHER;/*@V53040*/

      /* Determine if the adapter can address > 16MB     */
      if (!(npACB->AdapterFlags & AF_16M))
         npACB->AdapterFeatures |= ASPI_ADDRESS_LIMITED;

      npACB->MaximumSGList       = npAI->MaxHWSGList;                 /*@V53040*/
      npACB->MaximumCDBTransfer  = npAI->MaxCDBTransferLength;        /*@V53040*/
      npACB->AdapterFlags        = npAI->AdapterFlags;
      }
   return(npACB);
   }


/*********************************************************
*                                                        *
*   Procedure Name : BuildATE                            *
*                                                        *
*   Description : This procedure creates an adapter      *
*   target entry for the current unit                    *
*                                                        *
*   Input :                                              *
*         pADD_IORB_Entry - A pointer to the ADD entry   *
*            point for the current unit.                 *
*         npUI - A pointer to the information on the     *
*            current device.                             *
*         allocationStatus - A record of whether the     *
*            current device has already been allocated   *
*            by another device manager.                  *
*                                                        *
*   Output :   A pointer to the adapter target entry     *
*                                                        *
*********************************************************/
NPATE BuildATE(VOID (FAR * pADD_IORB_Entry) (PIORB), NPUNITINFO npUI,
               USHORT allocationStatus)

   {
   NPATE              npATE;
   NPDIREXCEPT        npDIRE;
   NPSCSI_INQDATA     npINQ;
   USHORT             i, n;

   /* Allocate memory for the data structure */
   if ((npATE = (NPATE) InitAllocate(sizeof(ATE))) != 0)
      {
      /* Remember which adapter owns this device */
      npATE->npOwnerACB = npLastACB;

      /* If this is the first adapter, make it the anchor */
      if (!npLastATE)
         npLastACB->npFirstATE = npATE;
      else
         npLastATE->npNextATE = npATE;

      /* Initialize all fields of the ATE */
      npLastATE = npATE;
      npATE->SRBActiveQueueCount = 0;
      npATE->npSRBActiveQueue = 0;
      npATE->TargetADD_Entry  = pADD_IORB_Entry;
      npATE->Flags            = 0;
      npATE->UnitHandle       = npUI->UnitHandle;
      npATE->QueuingCount     = npUI->QueuingCount;
      npATE->DeviceTargetID   = npUI->UnitSCSITargetID;
      npATE->DeviceTargetLUN  = npUI->UnitSCSILUN;
      npATE->DeviceType       = (UCHAR) npUI->UnitType;
      npATE->TargetFeatures   = 0;

      /**
       ** Select a DATA TRANSFER direction table based
       ** on the SCSI device type
       **/
      switch (npATE->DeviceType)
         {
          case UIB_TYPE_DISK:
          case UIB_TYPE_CDROM:
          case UIB_TYPE_WORM:
          case UIB_TYPE_OPTICAL_MEMORY:
            npATE->npDirTable = DirTableDASD;
            break;
          case UIB_TYPE_TAPE:
            npATE->npDirTable = DirTableTAPE;
            break;
          case UIB_TYPE_PROCESSOR:
            npATE->npDirTable = DirTablePROCESSOR;
            break;
          case UIB_TYPE_SCANNER:
            npATE->npDirTable = DirTableSCANNER;
            break;
          case UIB_TYPE_PRINTER:
            npATE->npDirTable = DirTablePRINTER;
            break;
          case UIB_TYPE_CHANGER:
            npATE->npDirTable = DirTableCHANGER;
            break;
          case UIB_TYPE_COMM:
            npATE->npDirTable = DirTableCOMM;
            break;
          default:
            npATE->npDirTable = 0;
          }

      /**
       ** Obtain device inquiry data and determine if this
       ** device matches anyone on the exception list
       **/
      if ( npINQ = GetInquiryData( npATE )  )
         {
         npDIRE = DirTableExceptions;
         npATE->npDirExcept = 0;

         for (i = 0; i < MaxDirExceptions; i++, npDIRE++ )
            {
            n = strlength((PSZ) npDIRE->npVendorID);
            if ( !strncmp( (PSZ) npDIRE->npVendorID, (PSZ) npINQ->VendorID, n ) )
               {
               continue;
               }
            n = strlength((PSZ) npDIRE->npProductID);
            if ( !strncmp( (PSZ) npDIRE->npProductID, (PSZ) npINQ->ProductID, n ) )
               {
               continue;
               }
            npATE->npDirExcept = npDIRE->npExceptList;
            }
         }

      /* Remember that another ASPI target was found */
      numberOfASPITargets++;

      /* Record the unit as allocated if the user wishes to share */
      /* allocation with another device manager that has already  */
      /* allocated the target. Remember that a device manager that */
      /* loads AFTER this one may allocate the target as well. */
      if (allocationStatus && allocationOverride)
         {
         npATE->Flags |= ATEF_IGNORE_ALLOCATION;
         npATE->TargetFeatures |= ASPI_DEVICE_ALLOCATION_SHARED;

         }

      /* If the user wishes to share on a command basis and deallocate */
      /* after each command, then record that here as well. */
      /* Since deallocating a target that was permanently allocated by */
      /* another manager may cause problems, we have to handle that too. */
      /* The simplest thing to do is check the allocation status and if */
      /* the device was already allocated, don't mark it as shared. */
      if (shareTargets && !allocationStatus)
         npATE->Flags |= ATEF_SHARE_ALLOCATION;
      }

   return(npATE);
   }

/*********************************************************
*                                                        *
*   Procedure Name : GetInquiryData                      *
*                                                        *
*                                                        *
*                                                        *
*                                                        *
*                                                        *
*********************************************************/

NPSCSI_INQDATA GetInquiryData( NPATE npATE )
   {

   NPSCSI_INQDATA    npInqBuf;
   NPBYTE            npCDB;
   NPSCATGATENTRY    npSGList;
   USHORT            rc, AllocStatus;

   memset((PBYTE) npIORBUnitControl,
          0,
          sizeof(IORB_UNIT_CONTROL));

   /* Set up IORB to check unit allocation */

   npIORBUnitControl->iorbh.Length          = sizeof(IORB_UNIT_CONTROL);
   npIORBUnitControl->iorbh.UnitHandle      = npATE->UnitHandle;
   npIORBUnitControl->iorbh.CommandCode     = IOCC_UNIT_CONTROL;
   npIORBUnitControl->iorbh.CommandModifier = IOCM_ALLOCATE_UNIT;
   npIORBUnitControl->iorbh.RequestControl  = IORB_ASYNC_POST;
   npIORBUnitControl->iorbh.Status          = 0;
   npIORBUnitControl->iorbh.ErrorCode       = 0;
   npIORBUnitControl->Flags                 = 0;
   npIORBUnitControl->iorbh.NotifyAddress   = &InitPost;

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

   memset((PBYTE) npIORBInq,
          0,
          sizeof(IORB_ADAPTER_PASSTHRU)+sizeof(SCSI_INQDATA));

   /**
    ** The Inquiry CDB and SGList are built in the IORB's
    ** DMWorkSpace. The Inquiry data buffer immediately follows
    ** the Adapter Passthru IORB.
    **/
   npCDB =    &npIORBInq->iorbh.DMWorkSpace[8];
   npSGList = (NPSCATGATENTRY) &npIORBInq->iorbh.DMWorkSpace[0];
   npInqBuf = (NPSCSI_INQDATA) (((NPBYTE) npIORBInq)
                                         + sizeof(IORB_ADAPTER_PASSTHRU));

   /**
    ** Setup the generic IORB fields
    **/
   npIORBInq->iorbh.Length          = sizeof(IORB_ADAPTER_PASSTHRU);
   npIORBInq->iorbh.UnitHandle      = npATE->UnitHandle;
   npIORBInq->iorbh.CommandCode     = IOCC_ADAPTER_PASSTHRU;
   npIORBInq->iorbh.CommandModifier = IOCM_EXECUTE_CDB;
   npIORBInq->iorbh.RequestControl  = IORB_ASYNC_POST;
   npIORBInq->iorbh.Status          = 0;
   npIORBInq->iorbh.ErrorCode       = 0;
   npIORBInq->iorbh.NotifyAddress   = &InitPost;

   /**
    ** Setup the Adapter Passthru IORB specific fields
    **/
   npIORBInq->cSGList               = 1;
   npIORBInq->pSGList               = npSGList;
   npIORBInq->ppSGLIST              = ppDataSeg + (USHORT) npSGList;
   npIORBInq->ControllerCmdLen      = 6;
   npIORBInq->pControllerCmd        = npCDB;
   npIORBInq->Flags                 = PT_DIRECTION_IN;

   /**
    ** Build the Inquiry CDB in the IORB DMWorkSpace
    **/
   npCDB[0] = 0x12;
   npCDB[1] = npATE->DeviceTargetLUN << 4;
   npCDB[4] = sizeof(SCSI_INQDATA);

   /**
    ** Build the single entry SGList in the IORB DMWorkSpace
    **/
   npSGList->ppXferBuf  = ppDataSeg + (USHORT) npInqBuf;
   npSGList->XferBufLen = sizeof(SCSI_INQDATA);


   rc = SendIORBandWait((NPIORB) npIORBInq,
                                 npATE->TargetADD_Entry);

   /* If the unit was unallocated, release it before returning */

   if (!AllocStatus)
      {
      npIORBUnitControl->iorbh.UnitHandle      = npATE->UnitHandle;
      npIORBUnitControl->iorbh.CommandModifier = IOCM_DEALLOCATE_UNIT;
      npIORBUnitControl->iorbh.RequestControl  = IORB_ASYNC_POST;
      npIORBUnitControl->iorbh.Status          = 0;
      npIORBUnitControl->iorbh.ErrorCode       = 0;
      npIORBUnitControl->Flags                 = 0;
      npIORBUnitControl->iorbh.NotifyAddress   = &InitPost;
      SendIORBandWait((NPIORB) npIORBUnitControl,
                               npATE->TargetADD_Entry);
      }

   return( (rc) ? 0 : npInqBuf );
   }


/*********************************************************
*                                                        *
*   Procedure Name : InitAllocate                        *
*                                                        *
*   Description : This procedure releases some of the    *
*   memory available for building data structures.       *
*                                                        *
*   Input :                                              *
*         Size - The number of bytes requested           *
*                                                        *
*   Output :   A pointer to the memory allocated. If no  *
*   memory is allocated, NULL is returned.               *
*                                                        *
*********************************************************/
NPVOID NEAR InitAllocate( USHORT Size )

   {
   NPVOID      PoolElement = 0;

   /* Is there enough memory left in the pool? */
   if (ConfigPoolAvail >= Size)
      {
      /* Create a pointer and remove the memory from the pool */
      PoolElement      = (NPVOID) npConfigPool;
      ConfigPoolAvail -= Size;
      npConfigPool    += Size;
      }

   return(PoolElement);
   }

/*********************************************************
*                                                        *
*   Procedure Name : InitSRBPool                         *
*                                                        *
*   Description : This procedure allocates memory for    *
*   all the SRBs that are available to all the ATE.      *
*                                                        *
*   Input :                                              *
*                                                        *
*   Output :                                             *
*                                                        *
*********************************************************/
VOID NEAR InitSRBPool(VOID)

   {
   NPACB    currentACB;
   NPATE    currentATE;
   NPBYTE   npSRBLink;
   USHORT   SRBPoolSize;
   USHORT   numberOfSRBs = 0;
   USHORT   i;

   /* Calculate the number of SRBs based on the queue count */
   /* for each ATE.                                         */
   currentACB = npACBAnchor;
   while (currentACB)
      {
      currentATE = currentACB->npFirstATE;
      while (currentATE)
         {
         if ((currentATE->QueuingCount > MAX_QUEUING_COUNT) ||
             (currentATE->QueuingCount == 0))
            numberOfSRBs += MAX_QUEUING_COUNT;
         else
            numberOfSRBs += currentATE->QueuingCount;

         currentATE = currentATE->npNextATE;
         }
      currentACB = currentACB->npNextACB;
      }

   freeSRBSpace = (USHORT) &InitDataStart - (USHORT) npConfigPool;

   /* Make sure that the pool of SRBs is not greater than the */
   /* space left in the data segment                          */
   SRBPoolSize = numberOfSRBs * sizeof(SRB_LINK);
   if ((ULONG) SRBPoolSize > (ULONG) freeSRBSpace)
      {
      numberOfSRBs = freeSRBSpace / sizeof(SRB_LINK);
      SRBPoolSize = numberOfSRBs * sizeof(SRB_LINK);
      }

   /* Initialize the SRB pool at the end of the ACB and ATE */
   npSRBLinkFreeList = (NPSRB_LINK) npConfigPool;
   npSRBLink = npConfigPool;

   for (i = 0;i < numberOfSRBs; i++)
      {
      /* Allocate one GDT selector for the SRB */                      /*@V58231*/
      DevHelp_AllocGDTSelector((PSEL) (&((NPSRB_LINK) npSRBLink)->SRBGDTSelector), /*@V58231*/
                               1);                                     /*@V58231*/
                                                                       /*@V58231*/
      /* Allocate one GDT selector for the SRB's scatter/gather list*/ /*@V58231*/
      DevHelp_AllocGDTSelector((PSEL) (&((NPSRB_LINK) npSRBLink)->SGGDTSelector),  /*@V58231*/
                               1);                                     /*@V58231*/

      ((NPSRB_LINK) npSRBLink)->npNextSRBLink = (NPSRB_LINK) (npSRBLink + sizeof(SRB_LINK));
      npSRBLink += sizeof(SRB_LINK);
      }

   ((PSRB_LINK) (npSRBLink - sizeof(SRB_LINK)))->npNextSRBLink = 0;

   /* Update the pointer to the end of valid data */

   npConfigPool += SRBPoolSize;
   }

/*********************************************************
*                                                        *
*   Procedure Name : ParseCmdLine                        *
*                                                        *
*   Description : This procedure parses all command line *
*   parameters that were specified.                      *
*                                                        *
*   Input :                                              *
*         pADDParms - A pointer to the command line      *
*                                                        *
*   Output : This procedure returns 0 if it is successful*
*   and a positive integer if an error is detected.      *
*                                                        *
*********************************************************/
USHORT ParseCmdLine(PDDD_PARM_LIST pADDParms)
   {
   CC            cc;
   PSZ           pOutBuf;


   USHORT        rc = 0;
   USHORT        Length;

   pOutBuf = poutbuf;

   cc = Command_Parser((PSZ) pADDParms,
                       (POPTIONTABLE) &opttable,
                       (PBYTE) pOutBuf,
                       (USHORT) outbuf_len);

   if (cc.ret_code == NO_ERR)
      {
      while (!rc && pOutBuf[1] != (UCHAR) TOK_ID_END)
         {
         Length = pOutBuf[0];

         switch (pOutBuf[1])
            {
            case TOK_ID_ALL:
               allocationOverride = TRUE;
               break;

            case TOK_ID_SHARE:
               shareTargets = TRUE;
               break;

            default:

               rc = 1;
               break;
            }

         if (!rc)
            pOutBuf += Length;

         }
      }
   else
      if (cc.ret_code != NO_OPTIONS_FND_ERR)
         rc = 1;

   return(rc);
   }


/*********************************************************
*                                                        *
*   Procedure Name : TTYWrite                            *
*                                                        *
*   Description : This procedure writes a message to the *
*   screen. Since the driver runs at ring 0 this must    *
*   be deferred until later and handled by the kernel.   *
*                                                        *
*   Input :                                              *
*         Buf - A pointer to the message to be displayed *
*                                                        *
*   Output : None                                        *
*                                                        *
*********************************************************/
VOID TTYWrite(PSZ Buf)
   {
   InitMsg.MsgStrings[0] = Buf;

   DevHelp_Save_Message((NPBYTE) &InitMsg);
   }


                                                                     /*@V64399*/
VOID FAR _loadds InitPost(PIORB pIORB)                               /*@V64399*/
{                                                                    /*@V64399*/
  USHORT    AwakeCount;

  DevHelp_ProcRun( (ULONG) OFFSETOF(pIORB), &AwakeCount );           /*@V64399*/
}                                                                    /*@V64399*/
