/*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.      */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = SCSIREQ1.C
 *
 * DESCRIPTIVE NAME = IBM2SCSI.ADD - Adapter Driver for IBM SCSI adapters.
 *                    IORB processing routines, file #1.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  This file implements the following IORB processing routines:
 *
 *              IOCC_CONFIGURATION
 *              IOCC_UNITCONTROL
 *              IOCC_GEOMETRY
 *
 *              And the IORB entry point IORBEntry (Called by upper level
 *              drivers.
 *
 *
*/

/*----------------------*/
/* System include files */
/*----------------------*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"

/*----------------------*/
/* ADD include files    */
/*----------------------*/

#include "strat2.h"
#include "dhcalls.h"
#include "iorb.h"
#include "scb.h"
#include "reqpkt.h"
#include "abios.h"
#include "scsi.h"

/*----------------------*/
/* Local include files  */
/*----------------------*/

#include "mmscb.h"
#include "delivery.h"
#include "scsiadd.h"
#include "scsiextn.h"
#include "scsipro.h"


/*************************************************************************/
/*                                                                       */
/* IORBEntry                                                             */
/*                                                                       */
/*    This is the IORB entry point.  A device manager or filter ADD will */
/*    call the ADD with a pointer to an IORB.  The input IORB may have   */
/*    other IORBs chained to it.  Each IORB is processed separately as   */
/*    if it was received by itself.                                      */
/*                                                                       */
/*       Entry:  IORBEntry(pIORB)                                        */
/*                                                                       */
/*               pIORB  - pointer to the IORB (header portion)           */
/*                                                                       */
/*       Exit:   None.                                                   */
/*                                                                       */
/*************************************************************************/

VOID FAR _loadds IORBEntry(PIORB pIORB)

{

   NPDCB     npDCB;                  /* device control block     */
   PIORB     pCurIORB, pNextIORB;
   BOOL      done;
   NPCALLTBL npCallTbl;

   pCurIORB = pIORB;
   done     = FALSE;

  /*-----------------------------------------------------------------------*/
  /* Loop through the IORB chain.  For each IORB pull it off the chain and */
  /* handle it as a separate request.                                      */
  /*-----------------------------------------------------------------------*/

   while (!done) {

      /*-----------------------------------------------------*/
      /* First determine if this is the last one (no chain   */
      /* bit set.  Regardless unlink this IORB from any that */
      /* may be chained to it, saving the ptr to the rest of */
      /* the chain first.  Then init out IORB work space.    */
      /*-----------------------------------------------------*/

      done = !(pCurIORB->RequestControl & IORB_CHAIN);  /* last one ?       */
      pNextIORB = pCurIORB->pNxtIORB;                   /* save next ptr    */
      pCurIORB->pNxtIORB = NULL;                        /* unlink this one  */
      pCurIORB->RequestControl &= ~IORB_CHAIN;          /* reset chain bit  */
      InitIORBWorkSpace(pCurIORB);

      TRACE(TR_RECEIVED, pCurIORB);                /* make a trace entry    */

      /*-----------------------------------------------------------------*/
      /* Now validate the command.  It will return a ptr to a DCB if one */
      /* is required and it was found.  If found then get the IORB call  */
      /* table from the DCB, otherwise use the default.  If the IORB is  */
      /* valid then the error code will 0.  If non-zero then end it.     */
      /* Otherwise processit.                                            */
      /*-----------------------------------------------------------------*/

      npDCB = ValidIORBCommand(pCurIORB);          /* validate command  */
      npCallTbl = (npDCB) ? npDCB->npCallTbl : IORBCallTbl;

      if (!pCurIORB->ErrorCode) {
         (*(npCallTbl+pCurIORB->CommandCode)->npFunc)(pCurIORB, npDCB);
      }
      else {
         FinishIORB(pCurIORB, npDCB);
      }

      pCurIORB = pNextIORB;         /* on to the next (if present)   */
   }
}


/*************************************************************************/
/*                                                                       */
/* IOCC_Configuration                                                    */
/*                                                                       */
/*    This routine is the router for the configuration call.             */
/*                                                                       */
/*       Entry:  IOCC_Configuration(pCfg, npNul)                         */
/*                                                                       */
/*               pCfg - ptr to the IORB                                  */
/*               npNul - place holder, no DCB available yet              */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/


VOID IOCC_Configuration(PIORB_CONFIGURATION pCfg, NPDCB npNul)
{

   /*-----------------------------------*/
   /* Determine which routine to call.  */
   /*-----------------------------------*/

   if (pCfg->iorbh.CommandModifier == IOCM_GET_DEVICE_TABLE) {
      IOCM_GetDeviceTable(pCfg, NULL);
   }
   else {
      IOCM_CompleteInit(pCfg, NULL);
   }
}

/*************************************************************************/
/*                                                                       */
/* IOCM_GetDeviceTable                                                   */
/*                                                                       */
/*    This routine builds the device configuration table for the caller. */
/*    See the IORB.H file for a description of the table that is built.  */
/*                                                                       */
/*       Entry:  IOCC_Configuration(pCfg, npNul)                         */
/*                                                                       */
/*               pCfg - ptr to the IORB                                  */
/*               npNul - place holder, no DCB available yet              */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/


VOID IOCM_GetDeviceTable(PIORB_CONFIGURATION pCfg, NPDCB npNul)

{
   USHORT       size, i, j, k, l;
   USHORT       UnitIndex;
   USHORT       cAdp;                /* calc'd count of adapters */
   NPBYTE       npFree;
   PBYTE        pCfgTbl, pData;
   PUNITINFO    pUnitTbl, pSwapUnit, p1stDiskUnit;
   PADAPTERINFO pAdpTbl;
   NPDCB        npDCB;
   UCHAR        drivenum = 0x80;
   UNITINFO     TempInfo;


   /*-------------------------------------------------------------------*/
   /* First make sure that there is enough space in the buffer to build */
   /* the device table.  Also figure out how many virtual adapters.     */
   /*-------------------------------------------------------------------*/

   cAdp = 0;
   for (i=0; i<cAdapters; i++) {
      cAdp += ACBTbl[i].cBus;
   }

   pCfgTbl = (PBYTE)pCfg->pDeviceTable;        /* ptr to the buffer  */
   size = sizeof(DEVICETABLE) + cAdp * sizeof(ADAPTERINFO) +
          (cDevices - cAdp) * sizeof(UNITINFO);

   if (size > pCfg->DeviceTableLen) {               /* not enough space  */
      pCfg->iorbh.ErrorCode = IOERR_CMD_SYNTAX;
   }

   /*--------------------------------------------------*/
   /* There is enough space so build the device table. */
   /*--------------------------------------------------*/

   else {

      /*--------------------------------------------*/
      /* Set the static fields of the device table. */
      /*--------------------------------------------*/

      ((PDEVICETABLE)pCfgTbl)->ADDLevelMajor = ADD_LEVEL_MAJOR;
      ((PDEVICETABLE)pCfgTbl)->ADDLevelMinor = ADD_LEVEL_MINOR;
      ((PDEVICETABLE)pCfgTbl)->ADDHandle     = ADDHandle;
      ((PDEVICETABLE)pCfgTbl)->TotalAdapters = cAdp;

      (PBYTE)pAdpTbl = (PBYTE)((PDEVICETABLE)pCfgTbl)->pAdapter +
                       (cAdp * sizeof(NPADAPTERINFO));

      /*------------------------------------------------------------------*/
      /* For each adapter build the adapter info device info structures.  */
      /* Also build virtual adapter info structures.                      */
      /*------------------------------------------------------------------*/

      cAdp = 0;
      for (i=0; i<cAdapters; i++) {

         for (l=0; l<ACBTbl[i].cBus; l++) {
            ((PDEVICETABLE)pCfgTbl)->pAdapter[cAdp++] = (NPADAPTERINFO)pAdpTbl;
            UnitIndex = 0;        /* reset this adapter's unitindex  */

            for (j=0; j<17; j++) {                        /* copy name  */
               pAdpTbl->AdapterName[j] = AdapterName[j];
            }

            /*----------------------------*/
            /* Set adapter hardware info. */
            /*----------------------------*/

            pAdpTbl->AdapterUnits         = 0;
            pAdpTbl->AdapterIOAccess      = AI_IOACCESS_BUS_MASTER;
            pAdpTbl->AdapterSCSITargetID  = ACBTbl[i].TargetID;
            pAdpTbl->AdapterSCSILUN       = 0;
            pAdpTbl->AdapterFlags         = AF_IBM_SCB + AF_HW_SCATGAT;
            pAdpTbl->AdapterFlags        |= ACBTbl[i].AFlags;
            pAdpTbl->AdapterHostBus       = AI_HOSTBUS_uCHNL;

            pAdpTbl->AdapterHostBus |= (pAdpTbl->AdapterFlags & AF_16M) ?
                                       AI_BUSWIDTH_32BIT : AI_BUSWIDTH_16BIT;

            pAdpTbl->AdapterDevBus        = AI_DEVBUS_SCSI_2;

            if (ACBTbl[i].id == AID_CORVETTE) {

               /* internal bus  */
               if (l == 0) {
                  pAdpTbl->AdapterDevBus |= (ACBTbl[i].iBus & BUS_WIDE) ?
                                 AI_DEVBUS_16BIT : AI_DEVBUS_8BIT;
                  pAdpTbl->AdapterDevBus |= (ACBTbl[i].iBus & BUS_FAST) ?
                                 AI_DEVBUS_FAST_SCSI : 0;
               }
               /* external bus  */
               else {
                  pAdpTbl->AdapterDevBus |= (ACBTbl[i].eBus & BUS_WIDE) ?
                                 AI_DEVBUS_16BIT : AI_DEVBUS_8BIT;
                  pAdpTbl->AdapterDevBus |= (ACBTbl[i].eBus & BUS_FAST) ?
                                 AI_DEVBUS_FAST_SCSI : 0;
               }
            }
            else {
               pAdpTbl->AdapterDevBus |= AI_DEVBUS_8BIT;
            }



            pAdpTbl->MaxHWSGList          = 16;
            pAdpTbl->MaxCDBTransferLength = 0;

            /*----------------------------------------------------------*/
            /* Set the virtual adapter bit if this is not the 1st bus.  */
            /*----------------------------------------------------------*/

            if (l > 0) {
               pAdpTbl->AdapterFlags |= AF_ASSOCIATED_DEVBUS;
            }

            pUnitTbl = pAdpTbl->UnitInfo;    /* start of unitinfo structures */

            /*------------------------------------------------------------*/
            /* For each device on this adapter and bus build the unitinfo */
            /* structure.                                                 */
            /*------------------------------------------------------------*/

            for (j=0; j<cDevices; j++) {
               npDCB = *(UnitHandleTbl + j);
               if ((npDCB->UnitInfo.AdapterIndex == i) && (npDCB->bus == l)) {

                  /*---------------------------------*/
                  /* save selectable boot swap info  */
                  /*---------------------------------*/

                  if (ADDStatus & SWITCHDRIVES &&
                      npDCB->UnitInfo.UnitType == UIB_TYPE_DISK) {
                     if (drivenum == 0x80) {
                        p1stDiskUnit = pUnitTbl;
                     }
                     if (drivenum == SelectableBootDrv) {
                        pSwapUnit = pUnitTbl;
                     }
                     drivenum++;
                  }

                  pAdpTbl->AdapterUnits++;

                  if (npDCB->status & UNITCHANGED) {         /* copy the data */
                     *pUnitTbl = *(npDCB->pChangedInfo);
                  }
                  else {
                     *pUnitTbl = npDCB->UnitInfo;
                  }

                  if (npDCB->status & UNITPOFF) {
                     pUnitTbl->UnitFlags |= UF_DEFECTIVE;
                  }

                  pUnitTbl->UnitIndex = UnitIndex++;
                  pUnitTbl++;
               }
            }

            (PBYTE)pAdpTbl += sizeof(ADAPTERINFO) +
                       ((pAdpTbl->AdapterUnits - 1) * sizeof(UNITINFO));
         }
      }


      /*------------------------------------------------------*/
      /* Swap the drive entries for selectable boot support.  */
      /*------------------------------------------------------*/

      if (ADDStatus & SWITCHDRIVES) {
         TempInfo      = *p1stDiskUnit;
         *p1stDiskUnit = *pSwapUnit;
         *pSwapUnit    = TempInfo;
      }

   }

   FinishIORB((PIORB)pCfg, NULL);

}


/*************************************************************************/
/*                                                                       */
/* IOCM_CompleteInit                                                     */
/*                                                                       */
/*    This routine is called to complete initialization when the system  */
/*    is switching over to using the newly loaded drivers.  It will kick */
/*    off the move mode initialization for all adapters that support it. */
/*                                                                       */
/*       Entry:  IOCM_CompleteInit(pCfg, npNul)                          */
/*                                                                       */
/*               pCfg  - ptr to the IORB                                 */
/*               npNul - NULL (not used, just a place holder)            */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/

VOID IOCM_CompleteInit(PIORB_CONFIGURATION pCfg, NPDCB npNul)

{

   USHORT i;

   INFMSG("Entered CompleteInit\n\r");
   DBSTOP();

   /*----------------------------------------------------------------*/
   /* Loop through all adapter control blocks and for each that      */
   /* indicates that move mode should be enabled, call InitMoveMode. */
   /* If move mode intialization fails then clear the enable move    */
   /* mode indicator and continue to operate the adapter in locate   */
   /* mode.  We may want to log an error here in the future.         */
   /*----------------------------------------------------------------*/

   for (i=0; i<cAdapters; i++) {
      if (ACBTbl[i].status & ENABLEMOVEMODE) {
         if (InitMoveMode(i)) {
            ACBTbl[i].status &= ~ENABLEMOVEMODE;
         }
      }
   }

   FinishIORB((PIORB)pCfg, NULL);

}


/*************************************************************************/
/*                                                                       */
/* IOCC_UnitControl                                                      */
/*                                                                       */
/*    This routine performs unit control.  There are three items that    */
/*    are supported.  Allocate, deallocate, and change.  This routine is */
/*    independent of the mode the adapter is operating in.               */
/*                                                                       */
/*       Entry:  IOCC_UnitControl(pUnitCtrl, npDCB)                      */
/*                                                                       */
/*               pUnitCtrl - ptr to the IORB                             */
/*               npDCB     - ptr to the DCB                              */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/


VOID IOCC_UnitControl(PIORB_UNIT_CONTROL pUnitCtrl, NPDCB npDCB)
{

   USHORT temp;           /* DO NOT REMOVE  */


   switch (pUnitCtrl->iorbh.CommandModifier) {

      /*-----------------------------------------------------------------*/
      /* This case is for allocating a unit.  A unit can be successfully */
      /* allocate if it is not yet allocated.                            */
      /*-----------------------------------------------------------------*/

      case IOCM_ALLOCATE_UNIT:

         if (!(npDCB->status & UNITALLOCATED)) {
            npDCB->status |= UNITALLOCATED;
         }
         else {
            pUnitCtrl->iorbh.ErrorCode = IOERR_UNIT_ALLOCATED;
         }
         break;


      /*-----------------------------------------------------------------*/
      /* This case is for deallocating a unit. A unit can be deallocated */
      /* if it is already allocated.                                     */
      /*-----------------------------------------------------------------*/

      case IOCM_DEALLOCATE_UNIT:
         if (npDCB->status & UNITALLOCATED) {
            npDCB->status &= ~UNITALLOCATED;
         }
         else {
            pUnitCtrl->iorbh.ErrorCode = IOERR_UNIT_NOT_ALLOCATED;
         }
         break;


      /*-----------------------------------------------------------------*/
      /* This case is to change the unit info for the specified unit.    */
      /* The ptr to the new info is saved and will be returned by to any */
      /* caller requesting the device info table.  We will always use the*/
      /* actual unit info the describes the device.                      */
      /*-----------------------------------------------------------------*/

      case IOCM_CHANGE_UNITINFO:
         if (npDCB->status & UNITALLOCATED) {
            if (pUnitCtrl->UnitInfoLen == sizeof(UNITINFO)) {
               npDCB->pChangedInfo = pUnitCtrl->pUnitInfo;
               npDCB->status |= UNITCHANGED;
            }
            else {
               pUnitCtrl->iorbh.ErrorCode = IOERR_CMD_SYNTAX;
            }
         }
         else {
            pUnitCtrl->iorbh.ErrorCode = IOERR_UNIT_NOT_ALLOCATED;
         }
         break;

   }

   FinishIORB((PIORB)pUnitCtrl, npDCB);

}

/*************************************************************************/
/*                                                                       */
/* IOCC_Geometry                                                         */
/*                                                                       */
/*    This is the main control routine for the geometry command to a     */
/*    locate mode device.  It will try to setup the command and issue    */
/*    it to the device.  If the command cannot be sent out then it is    */
/*    queued.                                                            */
/*                                                                       */
/*       Entry:  IOCC_Geometry(pGmtry, npDCB)                            */
/*                                                                       */
/*               pGmtry - get geometry IORB                              */
/*               npDCB  - ptr to the DCB                                 */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/

VOID IOCC_Geometry(PIORB_GEOMETRY pGmtry, NPDCB npDCB)

{

   USHORT rc;

   /*-------------------------------------------------------------------*/
   /* First verify that the type of geometry request is valid.  If so   */
   /* call the setup routine.  If the setup was successful and then see */
   /* if the device is idle.  If setup and IDLE then send the command   */
   /* to the device.  If setup was not successful or the device is not  */
   /* IDLE then queue the command.  The setup routine will set the      */
   /* callout address as required.                                      */
   /*-------------------------------------------------------------------*/

   if ((npDCB->UnitInfo.UnitType == UIB_TYPE_DISK  ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_WORM   ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_CDROM  ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_OPTICAL_MEMORY) &&
       pGmtry->iorbh.CommandModifier != IOCM_SET_MEDIA_GEOMETRY) {

      if (npDCB->status & UNITGMTRYSET) {                             /*@V95775*/
         pGmtry->pGeometry->NumHeads        = npDCB->cHeads;          /*@V95775*/
         pGmtry->pGeometry->SectorsPerTrack = npDCB->spt;             /*@V95775*/
         pGmtry->pGeometry->TotalCylinders  = npDCB->cCylinders;      /*@V95775*/
         pGmtry->pGeometry->BytesPerSector  = npDCB->cbps;            /*@V95775*/

         pGmtry->pGeometry->TotalSectors    = npDCB->cSectors;        /*REN9264*/

         FinishIORB((PIORB)pGmtry, npDCB);                            /*@V95775*/
      }                                                               /*@V95775*/
      else {
         rc = IOCM_GeometrySetup(pGmtry, npDCB);

         SAVEIF();
         if (rc == READY && npDCB->state == IDLE) {
            StartIORB((PIORB)pGmtry, npDCB);
         }
         else {
            QueueIORB((PIORB)pGmtry, npDCB, BACK);
         }

         RESTOREIF();

      }                                                               /*@V95775*/
   }
   else {
      pGmtry->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
      FinishIORB((PIORB)pGmtry, npDCB);
   }

}


/*************************************************************************/
/*                                                                       */
/* IOCM_GeometrySetup                                                    */
/*                                                                       */
/*    This routine sets up the SCB and ADD work space for the get device */
/*    geometry command to be issued.  It is for locate mode devices.     */
/*                                                                       */
/*       Entry:  IOCM_GeometrySetup(pGmtry, npDCB)                       */
/*                                                                       */
/*       Exit:   returns READY      if resources were allocated.         */
/*                       NORESOURCE if no resources were available.      */
/*                                                                       */
/*       Notes: This routine can be called at interrupt time.            */
/*              This routine is a call-out routine.                      */
/*                                                                       */
/*                                                                       */
/*************************************************************************/

USHORT IOCM_GeometrySetup(PIORB_GEOMETRY pGmtry, NPDCB npDCB)

{
   NPSCBH npSCBH;
   ULONG  ppGeometry;

   IORBWRKSP(pGmtry,Status)  &= ~IORBINCOMPLT;   /* assume all will be OK  */

   /*--------------------------------------------------------------------*/
   /* Try to allocate an SCB.  If allocated then build the SCB and setup */
   /* the IORB workspace accordingly.  Then return READY.                */
   /*--------------------------------------------------------------------*/

   if ((npSCBH = AllocSCB(npDCB, (PIORB)pGmtry)) != NULL) {
      ppGeometry = VirtToPhys((PBYTE)pGmtry->pGeometry);
      BuildSCB(&(npSCBH->scb), SCB_DEVICECAP, npDCB, 0L, ppGeometry, 8L, 0, 0);
      IORBWRKSP(pGmtry,Status)     |= IORBCALLOUT;
      IORBWRKSP(pGmtry,npSCBH)      = npSCBH;
      IORBWRKSP(pGmtry,cSCBs)       = 1;
      IORBWRKSP(pGmtry,npfnCallOut) = IOCM_GeometryDone;

      return(READY);
   }

   /*---------------------------------------------------------------------*/
   /* Otherwise no SCB is available.  Set the IORB status accordingly and */
   /* the callout address to this routine.  Then return NORESOURCE.       */
   /*---------------------------------------------------------------------*/

   else {
      IORBWRKSP(pGmtry,Status)  |= IORBCALLOUT+IORBINCOMPLT;
      IORBWRKSP(pGmtry,npfnCallOut)= IOCM_GeometrySetup;
      return(NORESOURCE);
   }

}

/*************************************************************************/
/*                                                                       */
/* IOCC_MM_Geometry                                                      */
/*                                                                       */
/*    This is the main control routine for the IOCC Geometry command for */
/*    a move mode device.                                                */
/*                                                                       */
/*       Entry:  IOCC_MM_Geometry(pGmtry, npDCB)                         */
/*                                                                       */
/*               pGmtry - get geometry IORB                              */
/*               npDCB  - ptr to the DCB                                 */
/*                                                                       */
/*       Exit:   none.                                                   */
/*                                                                       */
/*************************************************************************/

VOID IOCC_MM_Geometry(PIORB_GEOMETRY pGmtry, NPDCB npDCB)

{

   CE_READDEVCAP ce_readcap;
   ULONG         ppGeometry;


   /*-----------------------------------------------------------------*/
   /* Verify the type of geometry desired and the device type.  If it */
   /* is OK then build the control element and enqueue it.  If not OK */
   /* then set the errorcode and finish the IORB.                     */
   /*-----------------------------------------------------------------*/

   if ((npDCB->UnitInfo.UnitType == UIB_TYPE_DISK  ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_WORM   ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_CDROM  ||
       npDCB->UnitInfo.UnitType == UIB_TYPE_OPTICAL_MEMORY) &&
       pGmtry->iorbh.CommandModifier != IOCM_SET_MEDIA_GEOMETRY) {

      if (npDCB->status & UNITGMTRYSET) {                             /*@V95775*/
         pGmtry->pGeometry->NumHeads        = npDCB->cHeads;          /*@V95775*/
         pGmtry->pGeometry->SectorsPerTrack = npDCB->spt;             /*@V95775*/
         pGmtry->pGeometry->TotalCylinders  = npDCB->cCylinders;      /*@V95775*/
         pGmtry->pGeometry->BytesPerSector  = npDCB->cbps;            /*@V95775*/

         pGmtry->pGeometry->TotalSectors    = npDCB->cSectors;        /*REN9264*/

         FinishIORB((PIORB)pGmtry, npDCB);                            /*@V95775*/
      }                                                               /*@V95775*/
      else {                                                          /*@V95775*/
         ppGeometry = VirtToPhys((PBYTE) pGmtry->pGeometry);

         INITCTRLEL(ce_readcap, sizeof(CE_READDEVCAP),
                    FN_READCAP, npDCB, (PIORB)pGmtry);

         INITARSENSE(ce_readcap,pGmtry,npDCB)

         ce_readcap.ReqH.optctrl |= OP_RD;
         ce_readcap.ppBuf         = ppGeometry;
         ce_readcap.cSizeBuf      = 8L;

         IORBWRKSP(pGmtry,Status)     |= IORBCALLOUT;
         IORBWRKSP(pGmtry,Status)     &= ~IORBINCOMPLT;
         IORBWRKSP(pGmtry,npSCBH)      = NULL;
         IORBWRKSP(pGmtry,cSCBs)       = 1;
         IORBWRKSP(pGmtry,npfnCallOut) = IOCM_GeometryDone;

         StartCtrlEl((PREQH)&ce_readcap, npDCB);
      }                                                               /*@V95775*/
   }
   else {
      pGmtry->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
      FinishIORB((PIORB)pGmtry, npDCB);
   }
}


/*************************************************************************/
/*                                                                       */
/* IOCM_GeometryDone                                                     */
/*                                                                       */
/*    This routine completes the get geometry call.  The data returned   */
/*    from the device is used to generate geometry information on the    */
/*    device.  The routine is common to locate and move mode devices.    */
/*                                                                       */
/*       Entry:  IOCM_GeometryDone(pGmtry, npDCB)                        */
/*                                                                       */
/*       Exit:   COMPLETE - indicates that this request is complete      */
/*                                                                       */
/*       Notes: This routine could be called at interrupt time.          */
/*              This routine is a call-out routine.                      */
/*                                                                       */
/*************************************************************************/

USHORT IOCM_GeometryDone(PIORB_GEOMETRY pGmtry, NPDCB npDCB)
{

   UCHAR  temp_in[4], temp_out[4];
   ULONG  cRBAs, cCyls, cHeads, cSPT;
   PBYTE  pInByte;
   NPSCBH npSCBH;
   BOOL   ChkDsk;                                                       /*xxxxx*/

   IORB_EXECUTEIO ExecIO;
   SCATGATENTRY   ScatGatList;


   ChkDsk = FALSE;                                                     /*xxxxx*/

   /*-----------------------------------------------------*/
   /* The info returned from the device is byte reversed. */
   /* so reverse it into intel format.                    */
   /*-----------------------------------------------------*/

   (ULONG)*temp_in = pGmtry->pGeometry->TotalSectors;
   temp_out[0] = temp_in[3];
   temp_out[1] = temp_in[2];
   temp_out[2] = temp_in[1];
   temp_out[3] = temp_in[0];
   cRBAs       = *(PULONG)temp_out + 1;

   pInByte = (PBYTE)&(pGmtry->pGeometry->Reserved);
   temp_out[1] = *pInByte;
   temp_out[0] = *(pInByte + 1);

   pGmtry->pGeometry->BytesPerSector  = *(PUSHORT)temp_out;
   pGmtry->pGeometry->Reserved        = 0;

   /*------------------------------------------------------------*/
   /* For a 512 byte sector device generate CHS info.  For other */
   /* devices zero these fields out.                             */
   /*------------------------------------------------------------*/

   if (pGmtry->pGeometry->BytesPerSector == 512) {

      if (cRBAs < 2097152L) {             /* 0 - 1GB            */
         cHeads = 64L;
         cSPT   = 32L;
      }
      else if (cRBAs < 8257536L) {        /* 1GB - ~4GB (3.93)  */
         cHeads = 128L;
         cSPT   = 63L;                                                 /*xxxxx*/
         ChkDsk = TRUE;
      }
      else {                              /* > 4GB              */
         cHeads = 254L;                   /* REN 10/19/94        */
         cSPT   = 63L;                                                 /*xxxxx*/
         ChkDsk = TRUE;
      }

      cCyls = (cRBAs / (cHeads * cSPT));

      pGmtry->pGeometry->NumHeads        = (USHORT)cHeads;
      pGmtry->pGeometry->SectorsPerTrack = (USHORT)cSPT;
      pGmtry->pGeometry->TotalCylinders  = cCyls;
      pGmtry->pGeometry->TotalSectors    = cCyls * cHeads * cSPT;
   }

   else {
      pGmtry->pGeometry->NumHeads        = 0;
      pGmtry->pGeometry->SectorsPerTrack = 0;
      pGmtry->pGeometry->TotalCylinders  = 0;
      pGmtry->pGeometry->TotalSectors    = cRBAs;
   }

   /**************************************************************/
   /* If the disk is >= 1GB then check the boot sector for valid */
   /* Geometry.  This fixes a     where the above algorithm was  */
   /* used, but old ABIOS's put an older geometry on the disk.   */
   /* The above calculated incorrect geometry for those disks.   */
   /* Using that stored on the disk corrects the problem.        */
   /* We do this by generating an ExecuteIO IORB to ourselves.   */
   /**************************************************************/

   npDCB->status |= UNITGMTRYSET;                                     /*@V95775*/

   npDCB->cHeads      = pGmtry->pGeometry->NumHeads;                  /*REN9264*/
   npDCB->spt         = pGmtry->pGeometry->SectorsPerTrack;           /*REN9264*/
   npDCB->cCylinders  = pGmtry->pGeometry->TotalCylinders;            /*REN9264*/
   npDCB->cbps        = pGmtry->pGeometry->BytesPerSector;            /*REN9264*/
   npDCB->cSectors    = pGmtry->pGeometry->TotalSectors;              /*REN9264*/


   if (ChkDsk) {

      if ((npSCBH = AllocSCB(npDCB, (PIORB)pGmtry)) != NULL) {

         BuildSCB(&(npSCBH->scb), SCB_READ, npDCB, 0L,
                  ppData + (USHORT)&mbr, 512, 1, 512);
         IORBWRKSP(pGmtry,Status)     |= IORBCALLOUT;
         IORBWRKSP(pGmtry,npSCBH)      = npSCBH;
         IORBWRKSP(pGmtry,cSCBs)       = 1;
         IORBWRKSP(pGmtry,npfnCallOut) = IOCM_GeometryCheckBootSector;
         npSCBH->scb.Enable &= ~SCBEfPT;

      }
      pGmtry->pGeometry->TotalSectors    = cRBAs;   /* use original value  */

      return(INCOMPLETE);
   }
   return(COMPLETE);
}




/*************************************************************************/
/*                                                                       */
/* IOCM_GeometryCheckBootSector                                          */
/*                                                                       */
/*    This routine checks the boot sector geometry.  If it is valid then */
/*    it uses geometry calculated form this info.  If not valid then it  */
/*    does not change the info already set.                              */
/*                                                                       */
/*       Entry:  IOCM_GeometryCheckBootSector(pGmtry, npDCB)             */
/*                                                                       */
/*       Exit:   COMPLETE - indicates that this request is complete      */
/*                                                                       */
/*       Notes: This routine could be called at interrupt time.          */
/*              This routine is a call-out routine.                      */
/*                                                                       */
/*************************************************************************/


USHORT IOCM_GeometryCheckBootSector(PIORB_GEOMETRY pGmtry, NPDCB npDCB)

{

   ULONG  cRBAs;
   USHORT i, pt, cHead, spt;
   ULONG  cylSize, cCyl;

   BOOL   valid = FALSE;

   if (mbr.Signature == 0xaa55) {
      for (i=0; i<4; i++) {
         pt = mbr.PartitionTable[i].SysIndicator;
         if (!(pt == 0x00 || pt == 0x02 || pt == 0x03 || pt == 0x08 ||
               pt == 0x09 || pt == 0x63 || pt == 0x75 || pt == 0xee ||
               pt == 0xff))
         {
            valid = TRUE;
            break;
         }
      }

      if (valid) {
         cHead   = mbr.PartitionTable[i].EndHead + 1;
         spt     = mbr.PartitionTable[i].EndSector;
         cylSize = cHead * spt;

         cCyl = pGmtry->pGeometry->TotalSectors / cylSize;

         pGmtry->pGeometry->TotalCylinders  = cCyl;
         pGmtry->pGeometry->NumHeads        = cHead;
         pGmtry->pGeometry->SectorsPerTrack = spt;
         pGmtry->pGeometry->TotalSectors    = cCyl * cHead * spt;
      }
   }

   if (!valid) {

      if ((old_part == 1 && pGmtry->pGeometry->NumHeads > 64) ||
         (old_part == 2 && pGmtry->pGeometry->NumHeads > 128))
      {                                            /* REN 10/21/94 */
         if (old_part == 1) {                      /* REN 10/21/94 */
            cHead   = 64;                          /* REN 10/21/94 */
            spt     = 32;                          /* REN 10/21/94 */
         }                                         /* REN 10/21/94 */
         if (old_part == 2) {                      /* REN 10/21/94 */
            cHead   = 128;                         /* REN 10/21/94 */
            spt     = 63;                          /* REN 10/21/94 */
         }                                         /* REN 10/21/94 */

         cylSize = cHead * spt;                    /* REN 10/21/94 */

         cCyl = pGmtry->pGeometry->TotalSectors / cylSize; /* REN 10/21/94 */

         pGmtry->pGeometry->TotalCylinders  = cCyl;  /* REN 10/21/94 */
         pGmtry->pGeometry->NumHeads        = cHead; /* REN 10/21/94 */
         pGmtry->pGeometry->SectorsPerTrack = spt;   /* REN 10/21/94 */
         pGmtry->pGeometry->TotalSectors    = cCyl * cHead * spt; /* 10/21/94 */
      }
      else {
         pGmtry->pGeometry->TotalSectors = pGmtry->pGeometry->TotalCylinders *
                                           pGmtry->pGeometry->NumHeads *
                                           pGmtry->pGeometry->SectorsPerTrack;
      }
   }

   npDCB->cHeads      = pGmtry->pGeometry->NumHeads;                  /*REN9264*/
   npDCB->spt         = pGmtry->pGeometry->SectorsPerTrack;           /*REN9264*/
   npDCB->cCylinders  = pGmtry->pGeometry->TotalCylinders;            /*REN9264*/
   npDCB->cbps        = pGmtry->pGeometry->BytesPerSector;            /*REN9264*/
   npDCB->cSectors    = pGmtry->pGeometry->TotalSectors;              /*REN9264*/

   return(COMPLETE);
}
