/*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 = "%w% %e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = S506INIT.C
 *
 * DESCRIPTIVE NAME = IBM1S506.ADD - Adapter Driver for ST506/IDE DASD
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Adapter Driver initialization routines.
 *
 * Purpose:
 *
 *
 *
 *
 *
*/
 #define INCL_NOBASEAPI
 #define INCL_NOPMAPI
 #define INCL_NO_SCB
 #define INCL_INITRP_ONLY
 #define INCL_DOSERRORS
 #include "os2.h"
 #include "dos.h"
 #include "bseerr.h"
 #include "dskinit.h"
 #include <scb.h>
 #include <abios.h>

 #include "iorb.h"
 #include "reqpkt.h"
 #include "dhcalls.h"
 #include "addcalls.h"
 #include "devclass.h"

 #include "s506cons.h"
 #include "s506type.h"
 #include "s506regs.h"
 #include "s506ext.h"
 #include "s506pro.h"
 #include "s506misc.h"
 #include "s506ps2.h"

static strnswap( PSZ d, PSZ s, USHORT n );                           /*@V75103*/
static SHORT MemCmp( PBYTE s1, PBYTE s2, USHORT n );                /*@V108555*/

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID NEAR DriveInit(PRPINITIN pRPH )
{
  PRPINITIN             pRPI = (PRPINITIN)  pRPH;
  PRPINITOUT            pRPO = (PRPINITOUT) pRPH;
  NPATBL                npAT = AdapterTable;
  NPACB                 npACB;
  PMACHINE_CONFIG_INFO  pMCHI;
  ULONG                 TimeOut;
  USHORT                i;
  USHORT                j;
  USHORT                k;
  USHORT                rc;
  USHORT                Lid = -1;
  USHORT                DriveId;

  Device_Help = pRPH->DevHlpEP;

  /*-----------------------------------------*/                     /*@V98451*/
  /* Put name from Config.sys in DrvrName of */                     /*@V98451*/
  /* the DriverStruct structure.             */                     /*@V98451*/
  /*-----------------------------------------*/                     /*@V98451*/
                                                                    /*@V98451*/
  RMGetDriverName (pRPI, DriverStruct.DrvrName, DrvrNameSize);      /*@V98451*/
                                                                    /*@V98451*/
  /*---------------------------------------------------*/           /*@V98451*/
  /* Allocate Driver Handle                            */           /*@V98451*/
  /*                                                   */           /*@V98451*/
  /* Most of this data is 'canned' and is discard      */           /*@V98451*/
  /* after initialization completes.                   */           /*@V98451*/
  /*---------------------------------------------------*/           /*@V98451*/
  if(RMCreateDriver(&DriverStruct,                                  /*@V98451*/
                    &hDriver))                                      /*@V98451*/
  {                                                                 /*@V98451*/
     _asm int 3                                                     /*@V98451*/
  }                                                                 /*@V98451*/

  pDDD_Parm_List = (PDDD_PARM_LIST) pRPI->InitArgs;

  InitActive = 1;

  /*-----------------------------------------------*/
  /* Transfer information from Machine Info packet */
  /*-----------------------------------------------*/
  pMCHI = MAKEP( SELECTOROF(pDDD_Parm_List),
                 (USHORT)   pDDD_Parm_List->machine_config_table );

  MachineID = (pMCHI->Model << 8) | pMCHI->SubModel;


  /*------------------------------------------*/
  /* If this is a uChannel machine, deinstall */
  /* this driver.                             */
  /*------------------------------------------*/
  if ( pMCHI->BusInfo & BUSINFO_MCA )
  {
    rc = 1;
    goto S506_Deinstall;
  }

  rc = 0;

  /*-----------------------------------------*/                     /*@V106915*/
  /* Check for DPT adapters which require    */                     /*@V106915*/
  /* A status read in order to clear an      */                     /*@V106915*/
  /* interrupt                               */                     /*@V106915*/
  /*-----------------------------------------*/                     /*@V106915*/
  if ( DPT_Present() )                                              /*@V106915*/
  {                                                                 /*@V106915*/
     for (i=0; i< MAX_ADAPTERS; i++, npAT++ )                       /*@V106915*/
     {                                                              /*@V106915*/
        npAT->Flags |= ATBF_IRQCLEAR;                               /*@V106915*/
     }                                                              /*@V106915*/
     npAT = AdapterTable;                                           /*@V106915*/
  }                                                                 /*@V106915*/


  /*--------------------------------------*/
  /* Modify defaults in ADAPTER TABLE per */
  /* command line parameters.             */
  /*--------------------------------------*/

  if ( ParseCmdLine( pDDD_Parm_List, npAT ) )
  {
    TTYWrite( ParmErrMsg );
  }

  ADD_InitTimer( TimerPool, sizeof(TimerPool));

  /*----------------------------------------*/
  /* Setup each Adapter. Scan each adapter  */
  /* for drives (units).                    */
  /*----------------------------------------*/
  DriveId = ScanForOtherADDs() + 0x80;

  for (i=0; i< MAX_ADAPTERS; i++, npAT++ )
  {
    if ( !(npAT->Flags & ATBF_DISABLED) )
    {
      if ( !ConfigureController( npAT ) )
      {
        for (j=0; j < MAX_UNITS; j++, DriveId++ )
        {
          npAT->Unit[j].DriveId = DriveId;
                                                                    /*VVVVVVVV*/
          if ( ConfigureATAUnit( npAT, j ) )                        /*@V108783*/
          {
            if ( DoATAPIPresenseCheck( npAT, j ) )
            {
              if (!j)
              /*---------------------------------------------*/
              /* No Maser Unit was found, see if there is a  */
              /* slave ATAPI unit.                           */
              /*---------------------------------------------*/
              {
                j++;
                npAT->npACB->UnitCB[0].Flags &= ~UCBF_IDENTIFYFAIL;

                /*-----------------------------------------*/
                /* If no master unit, no diag info will    */
                /* be available to a reset.                */
                /*-----------------------------------------*/
                npAT->Flags |= ATBF_DISABLERESET;
                npAT->npACB->Flags |= ACBF_DISABLERESET;

                ATAPISlaveChk = 1;                                  /*@V109483*/

                if ( !DoATAPIPresenseCheck( npAT, j ) )
                {
                  npAT->Unit[0].Status = UTS_NO_MASTER;
                  npAT->npACB->UnitCB[0].Flags |= UCBF_NODASDSUPPORT;
                  npAT->npACB->cUnits++;
                  npAT->cUnits++;
                  cUnits++;
                }

                ATAPISlaveChk = 0;                                  /*@V109483*/

              }
            }                                                       /*@V108783*/
          }                                                         /*AAAAAAAA*/
        }

        npACB = npAT->npACB;

        for (j=0; j < npACB->cUnits; j++)
        {
          npACB->DelayedRetryInterval = DELAYED_RETRY_INTERVAL;
          npACB->IRQTimeOut           = IRQ_TIMEOUT_INTERVAL;

          if ( (TimeOut=npAT->Unit[j].TimeOut) < MIN_USER_TIMEOUT )
          {
            TimeOut = DEFAULT_USER_TIMEOUT;
          }
          npACB->TimeOut = 1000L * TimeOut;

        }

        CheckACBViable( npAT );                                      /*@V90963*/

      }
    }
    else
    {
      npAT->Status = ATS_SKIPPED;
    }
  }


  /*----------------------------------------------*/
  /* Do a final read verify on each unit to clear */
  /* any pending Recalibrate or Set Parameter     */
  /* requests                                     */
  /*----------------------------------------------*/
  InitIOComplete = 1;

  for (i=0; i < cAdapters; i++ )
  {
    npACB = ACBPtrs[i].npACB;

    for (j=0; j < npACB->cUnits; j++ )
    {
      if ( !(npACB->UnitCB[j].Flags & UCBF_ATAPI_DEVICE) &&        /*@V87325*/
           !(npACB->UnitCB[j].Flags & UCBF_NODASDSUPPORT ) )      /*@V108783*/
      {
        ReadDrive( npACB, j, 0, 1, NULL );                          /*@V108555*/
      }                                                            /*@V87325*/
    }
  }

  if ( Verbose )
  {
    PrintInfo( AdapterTable );
  }

  if ( !cUnits )
  {
    ADD_DeInstallTimer();

    rc = 1;
    goto S506_Deinstall;
  }


  pRPO->CodeEnd     = (USHORT) &DriveInit;
  pRPO->DataEnd     = (USHORT) npACBPool;
  pRPO->rph.Status  = STDON;


  if ( DevHelp_RegisterDeviceClass( (NPSZ)     AdapterName,
                                    (PFN)      &ADDEntryPoint,
                                    (USHORT)   0,
                                    (USHORT)   1,
                                    (PUSHORT)  &ADDHandle      )  )
  {
    _asm { int 3 }
  }

  AssignResources();                                                /*@V98451*/

S506_Deinstall:

  if ( rc )
  {
    pRPO->CodeEnd     = 0;
    pRPO->DataEnd     = 0;
    pRPO->rph.Status  = STDON + ERROR_I24_QUIET_INIT_FAIL;

    RMDestroyDriver(hDriver);                                       /*@V98451*/
  }

  InitActive = 0;
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT NEAR ConfigureController( NPATBL npAT )
{
  NPACB          npACB;
  USHORT         rc = 0;
  USHORT         i;
  BOOL           ATAPIPresent;                                     /*@V108783*/

  if (AllocAdapterResources(npAT))                                  /*@V98451*/
  {                                                                 /*@V98451*/
     /* AllocAdapterResources will assign the status to     */      /*@V98451*/
     /* npAT->Status for a failure on a resource allocation */      /*@V98451*/
                                                                    /*@V98451*/
     goto ConfigureControllerExit;                                  /*@V98451*/
  }                                                                 /*@V98451*/

  npACB = npAT->npACB = npACBPool;

  npACBPool++;

  ACBPtrs[cAdapters].npACB    = npACB;
  ACBPtrs[cAdapters].IRQLevel = npAT->IRQLevel;                    /*@V87325*/

  setmem((PBYTE) npACB, 0, sizeof(ACB) );

  for (i = FI_PDAT; i < FI_RFDR; i++ )
  {
    npACB->IOPorts[i] = npAT->BasePort + i;
  }
  npACB->IOPorts[FI_PERR]  = npACB->IOPorts[FI_PWRP];
  npACB->IOPorts[FI_PSTAT] = npACB->IOPorts[FI_PCMD];

  /*--------------------------------------------------------*/       /*@V64402*/
  /* Quantum Hardcards can use a Base Port of 0320. This    */       /*@V64402*/
  /* causes the Device Control Register (03x6) to be mapped */       /*@V64402*/
  /* at the same address as the Drive/Head register (01x6). */       /*@V64402*/
  /* Quantum uses address 032E as an alternate address.     */       /*@V64402*/
  /* for the Device Control Register.                       */       /*@V64402*/
  /*--------------------------------------------------------*/       /*@V64402*/
                                                                     /*@V64402*/
  npACB->IOPorts[FI_RFDR] =                                          /*@V64402*/
                  npACB->IOPorts[FI_PDRHD] |                         /*@V64402*/
                     ((npAT->BasePort & 0x0200) ? 0x0308 : 0x0300);  /*@V64402*/

  npACB->IORegs[FI_RFDR]  = 0x08;

  npACB->IntLevel   = npAT->IRQLevel;

  npACB->TimeOut              = INIT_TIMEOUT_SHORT;
  npACB->IRQTimeOut           = INIT_TIMEOUT_SHORT;
  npACB->DelayedRetryInterval = DELAYED_RETRY_INTERVAL;
  npACB->DelayedResetInterval = DELAYED_RESET_INTERVAL;
  npACB->IRQEntry             = ACBPtrs[cAdapters].IRQHandler;

  for (i=0; i < MAX_UNITS; i++ )
  {
    npACB->UnitCB[i].UnitId = i;
    npACB->UnitCB[i].npACB  = npACB;
  }

  npACB->IOSGPtrs.iPortAddress = npACB->IOPorts[FI_PDAT];

  /*----------------------------------------------------------*/   /*@V108783*/
  /* If an ATAPI device is set as slave and is the only       */   /*@V108783*/
  /* unit attached, a reset will a very long to time out.     */   /*@V108783*/
  /* CheckController will erase the first byte of the         */   /*@V108783*/
  /* signature, therefore it's existance must be checked here */   /*@V108783*/
  /*----------------------------------------------------------*/   /*@V108783*/
  ATAPIPresent =  !( (CheckForATAPISignature( npAT, 0)) &&         /*@V108783*/
                     (CheckForATAPISignature( npAT, 1))    );      /*@V108783*/

  if ( CheckController( npAT ) && !(npAT->Flags & ATBF_ATAPIPRESENT) )
  {
    npAT->Status = ATS_NOT_PRESENT;
    goto ConfigureControllerExit;
  }

  if ( npAT->Flags & ATBF_PS2IDEPORT )
  {
    npACB->Flags |= ACBF_PS2IDEPORT;
  }

  if ( npAT->Flags & ATBF_DISABLERESET )
  {
    npACB->Flags |= ACBF_DISABLERESET;
  }

  if ( npAT->Flags & ATBF_IRQCLEAR )                                /*@V106915*/
  {                                                                 /*@V106915*/
     npACB->Flags |= ACBF_IRQCLEAR;                                 /*@V106915*/
  }                                                                 /*@V106915*/

  if ( !IODelayCount )                                               /*@V90963*/
  {
    CalibrateTimers( npACB );
  }

  cAdapters++;                                                       /*@V90963*/

  /*---------------------------------------------*/                /*@V87325*/
  /* Activate the ACB                            */                /*@V87325*/
  /*                                             */                /*@V87325*/
  /* This Hooks the requested IRQ level if not   */                /*@V87325*/
  /* already hooked                              */                /*@V87325*/
  /*---------------------------------------------*/                /*@V87325*/
                                                                   /*@V87325*/
  if ( ActivateACB( npACB ) )                                      /*@V87325*/
  {                                                                /*@V87325*/
     npAT->Status = ATS_SET_IRQ_FAILED;                            /*@V87325*/
     goto ConfigureControllerExit;                                 /*@V87325*/
  }                                                                /*@V87325*/

  if ( !(npAT->Flags & ATBF_DISABLERESET) && (npAT->BasePort != FX_PRIMARY) )
  {
    if (!ATAPIPresent)                                             /*@V108783*/
    {                                                              /*@V108783*/
      ResetController( npACB );
    }                                                              /*@V108783*/
  }

ConfigureControllerExit:
  if (( npAT->Status ) && !( npAT->Flags & ATBF_ATAPIPRESENT ))     /*@V108555*/
     DeallocAdapterResources( npAT );

  return ( npAT->Status );
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT NEAR CheckController( NPATBL npAT )
{
  USHORT        rc;

  if ( npAT->BasePort == FX_PRIMARY )
  {
    SetupPS2IDEPort( 0 );

    if ( rc=CheckCylinderLReg( npAT, 0 ) )                         /*@V93625*/
    {
      SetupPS2IDEPort( 1 );

      if ( !(rc=CheckCylinderLReg( npAT, 0 )) )                    /*@V93625*/
      {
        npAT->Flags |= ATBF_PS2IDEPORT;
      }
    }
    else
    {
      SetupPS2IDEPort( 0 );
    }
  }
  else
  {
    rc = CheckCylinderLReg( npAT, 0 );                             /*@V93625*/
  }

  if ( rc )                                                       /*@V108783*/
  {                                                               /*@V108783*/
     DISABLE                                                      /*@V108783*/
     rc=CheckCylinderLReg( npAT, 1 );                             /*@V108783*/
     ENABLE                                                       /*@V108783*/
  }                                                               /*@V108783*/

  return ( rc );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT NEAR CheckCylinderLReg( NPATBL npAT, USHORT UnitId )        /*@V93625*/
{
  USHORT        i;
  USHORT        j;
  USHORT        i_Inv;                                            /*@V106915*/
  USHORT        Port;
  USHORT        SectorReg;                                        /*@V106915*/
  USHORT        OldData;


  /*---------------------------*/
  /* Insure UnitId is selected */
  /*---------------------------*/
  Port = npAT->BasePort + 6;
  i = (UnitId) ?  0xB0 : 0xA0; /* Select Unit */                   /*@V93625*/
  outp( Port, i);
  IODelay();

  /*--------------------*/                                         /*@V87325*/
  /* Save Register Data */                                         /*@V87325*/
  /*--------------------*/                                         /*@V87325*/
  inp( Port, OldData);                                             /*@V87325*/
  IODelay();                                                       /*@V87325*/

  /*----------------------------------------------*/
  /* Read/Write data patterns to the Cylinder reg */
  /*----------------------------------------------*/
  Port = npAT->BasePort + 4;                                       /*@V87325*/
  SectorReg = npAT->BasePort + 2;                                 /*@V106915*/

  i = -1;

  do
  {
    i++;
    outp( Port, i);
    IODelay();
    /*----------------------------------------------------------------------*/
    /* output the bitwise or of the data to another register.  This ensures */
    /* the data that we read back in the in is from the register and not    */
    /* transient data on the bus.                                           */
    /*----------------------------------------------------------------------*/
    outp( SectorReg, i_Inv );                                     /*@V106915*/
    IODelay();                                                    /*@V106915*/
    inp( Port, j );
    IODelay();
  }
  while ( i < 0x81 && i == j );

  /*-----------------------*/                                      /*@V87325*/
  /* Restore Register Data */                                      /*@V87325*/
  /*-----------------------*/                                      /*@V87325*/
  outp( Port, OldData);                                            /*@V87325*/
  IODelay();                                                       /*@V87325*/

  return ( !(i == j) );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
VOID NEAR CalibrateTimers( NPACB npACB )
{
  WaitDRQCount    = CALIBRATE_LOOP_COUNT;
  CheckReadyCount = CALIBRATE_LOOP_COUNT;
  IODelayCount    = CALIBRATE_LOOP_COUNT;

  WaitDRQCount    = CalibrateWorker( npACB, &WaitDRQ )    * MAX_WAIT_DRQ;
  CheckReadyCount = CalibrateWorker( npACB, &CheckReady ) * MAX_WAIT_READY;
  IODelayCount    = CalibrateWorker( npACB, &IODelay    ) / 1000l;


  if ( !WaitDRQCount    ) WaitDRQCount    = 100;
  if ( !CheckReadyCount ) CheckReadyCount = 100;
  if ( !IODelayCount    ) IODelayCount    = 1;

  if ( ADD_StartTimerMS((PULONG) &ElapsedTimerHandle,
                        (ULONG)  ELAPSED_TIMER_INTERVAL,
                        (PFN)    ElapsedTimer,
                        (ULONG)  0,
                        (ULONG)  0              ) )
  {
     _asm { int 3 }
  }
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
ULONG NEAR CalibrateWorker( NPACB npACB, NPCV npWorker )
{
  ULONG         CallCount      = 0;

  Calibrate      = 1;
  CallWorker     = 1;
  CallWorkerSync = 1;

  if ( ADD_StartTimerMS((PULONG) &CalibrateTimerHandle,
                        (ULONG)  CALIBRATE_TIMER_INTERVAL,
                        (PFN)    CalibrateTimer,
                        (ULONG)  0,
                        (ULONG)  0              ) )
  {
     _asm { int 3 }
  }


  while ( CallWorkerSync )
    ;

  while ( CallWorker )
  {
    (*npWorker)( npACB );

    CallCount++;
  }

  ADD_CancelTimer( CalibrateTimerHandle );

  Calibrate = 0;

  CallCount *= CALIBRATE_LOOP_COUNT;

  return ( CallCount / CALIBRATE_TIMER_INTERVAL );
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
VOID FAR CalibrateTimer( ULONG hCalibrateTimer, ULONG Unused1, ULONG Unused2 )
{
  if ( CallWorkerSync )
  {
    CallWorkerSync = 0;
  }
  else
  {
    CallWorker = 0;
  }
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT ConfigureATAUnit( NPATBL npAT, USHORT UnitId )
{
  NPACB         npACB;
  NPGEOMETRY    npGEO;
  USHORT        rc;

  npACB = npAT->npACB;

  /*----------------------------------------*/
  /* Select unit and check status for READY */
  /*----------------------------------------*/

  /*------------------------------------------*/
  /* COMPAQ machines throw an interrupt when  */
  /* a non-installed unit is selected.        */
  /*------------------------------------------*/

  DISABLE
  SelectUnit( npACB, UnitId );                                     /*@V87325*/
  rc = CheckReady( npACB);
  SelectUnit( npACB, 0 );                                          /*@V87325*/
  ENABLE

  if ( rc )
  {
    rc = npAT->Unit[UnitId].Status = UTS_NOT_READY;
  }
  else if ( !(rc=DetermineUnitGeometry( npAT, UnitId )) )
  {
    npACB->cUnits++;
    npAT->cUnits++;
    cUnits++;
  }

  return ( rc );
}

                                                                   /*@V87325*/
                                                                   /*@VVVVVV*/
/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT NEAR GetReg( NPACB npACB, USHORT Reg )
{
  USHORT        Port;
  USHORT        Data;

  Port = npACB->IOPorts[Reg];
  inp( Port, Data )
  return( Data );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT CheckForATAPISignature( NPATBL npAT, USHORT UnitId )
{
   USHORT Port;
   USHORT Data;
   USHORT rc = 1;
   NPACB  npACB;

   npACB = npAT->npACB;
   SelectUnit ( npACB, UnitId );
   Port = npAT->BasePort + FI_PCYLL;
   inp( Port, Data );
   IODelay();
   if ( Data==ATAPISIGL )
   {
      Port = npAT->BasePort + FI_PCYLH;
      inp( Port, Data );
      IODelay();
      if ( Data==ATAPISIGH )
      {
         rc = 0;
      }
   }
   return( rc );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT DoATAPIPresenseCheck( NPATBL npAT, USHORT UnitId )
{
  NPACB                 npACB;
  NPUCB                 npUCB;
  NPUTBL                npUT;
  USHORT                rc = 1; /* default is fail */
  USHORT                DevicePresent = 0; /* default is not present */
  USHORT                TestReg;
  USHORT                i;
  NPIDENTIFYDATA        npID;
  NPGENCONFIGWORD       npGenConfig;
  UCHAR                 TryReset = 0;


  npACB = npAT->npACB;
  npUCB = &npACB->UnitCB[UnitId];
  npUT  = &npAT->Unit[UnitId];

  npACB->TimeOut    = INIT_TIMEOUT_SHORT;
  npACB->IRQTimeOut = INIT_TIMEOUT_SHORT;

  /* Some ATAPI Devices / IDE Card combinations cause infinite interrupts when
     the ATAPI device is the master and no slave is present and the slave is
     selected via the drive select register.  A single interrupt will be
     processed if interrupts are disabled while the registers of the device are
     checked.  A non-existing device will report 0xFF in all registers. */

  DISABLE
  SelectUnit ( npACB, UnitId );

  DevicePresent=!CheckCylinderLReg( npAT, UnitId );                /*@V93625*/

  if ((!DevicePresent) && !(npUT->Flags & UTBF_ATAPIDEVICE))
  {
     SelectUnit( npACB, 0 );
     ENABLE
     goto DoATAPIPresenseCheckExit;
  }

  ENABLE

  npID = (NPIDENTIFYDATA) ScratchBuf;

  /* Identify needs to know to identify ATAPI */
  npUCB->Flags |= UCBF_ATAPI_DEVICE;

  /* Clear any pending requests  created from the first Identify in
     ConfigureATAUnit                                                 */
  npUCB->ReqFlags = 0;

  do
  {
    if ( TryReset )
    {
       DISABLE
       SelectUnit( npACB, UnitId );

       if ( !(npAT->Flags & ATBF_DISABLERESET) || ATAPISlaveChk )    /*@V93625*/
                                                                    /*@V109483*/
       {                                                             /*@V93625*/
         ResetController( npACB );                                   /*@V93625*/
         if (GetStatusReg( npACB ) & FX_ERROR) /*Probable DPT */     /*@V93625*/
         {                                                           /*@V93625*/
            SelectUnit( npACB, 0 );                                  /*@V93625*/
            ENABLE                                                   /*@V93625*/
            goto DoATAPIPresenseCheckExit;                           /*@V93625*/
         }                                                           /*@V93625*/
       }                                                             /*@V93625*/

       if ( ATAPIReset( npACB ) )
       {
          SelectUnit( npACB, 0 );
       }
       ENABLE
       TryReset = FALSE;
    }
    else
    {
       TryReset = TRUE;
    }

    if ( (npUT->Flags & UTBF_ATAPIDEVICE) ||
         DoIdentify(npACB, UnitId, (PBYTE) npID ) )
    {
       /* A DoIdentify Failure will select unit 0 */                /*@V108783*/
       SelectUnit( npACB, UnitId );                                 /*@V108783*/

       /* ATAPI Units can take several ms to show the signature */  /*@V108783*/
       for ( i=0;i < ATAPI_RECOVERY; i++ )                          /*@V108783*/
       {                                                            /*@V108783*/
         IODelay();                                                 /*@V108783*/
       }                                                            /*@V108783*/

       if ( !(rc=CheckForATAPISignature( npAT, UnitId ) ) ||
             (npUT->Flags & UTBF_ATAPIDEVICE) )
       {
          npAT->Unit[UnitId].Status = UTS_OK;
          npAT->Unit[UnitId].Flags |= UTBF_ATAPIDEVICE;             /*@V108783*/
          npACB->UnitCB[UnitId].Flags |= UCBF_ATAPI_DEVICE;
          npACB->cUnits++;
          npAT->cUnits++;
          cUnits++;

          /* force good return code if case UTBF_ATAPIDEVICE flag set */
          rc = 0;

          /* Some ATAPI units show FF in all registers until initialized. */
          /* We need to select unit 0 in case the HD is there so that it  */
          /* does not see the bsy caused by ff                            */
          SelectUnit( npACB, 0 );
       }
    }
    else
    {
       rc = 1;
    }

  } while ( rc && TryReset );

  DoATAPIPresenseCheckExit:

  if ( rc )
  {
    npUCB->Flags &= ~UCBF_ATAPI_DEVICE;
    npAT->Unit[UnitId].Status = UTS_NOT_PRESENT;
  }

  return ( rc );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT ATAPIReset( NPACB npACB )
{
   USHORT rc = 0;
   USHORT Data;
   USHORT Port;
   USHORT i;

   Data = FX_ATAPI_RESET;
   Port = npACB->IOPorts[FI_PCMD];
   outp( Port, Data );

   for ( i=0; i<8; i++ )
   {
      rc = CheckReady( npACB );
      if ( !rc )
      {
         break;
      }
   }

   return( rc );
}

                                                                   /*@AAAAAA*/
                                                                   /*@V87325*/
/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT DetermineUnitGeometry( NPATBL npAT, USHORT UnitId )
{
  NPACB                 npACB;
  NPUCB                 npUCB;
  NPUTBL                npUT;
  NPIORB_EXECUTEIO      npTI;
  NPIDENTIFYDATA        npID;
  NPGEOMETRY            npGEO;
  NPGEOMETRY            npAGEO;

  ULONG                 MaxSec;
  ULONG                 SecPerCyl;

  USHORT                rc = 0;
  USHORT                cSec;

  USHORT                IDEGeomUsed = 0;

  npACB = npAT->npACB;

  npUCB = &npACB->UnitCB[UnitId];
  npUT  = &npAT->Unit[UnitId];

  if ( npUT->Flags & UTBF_NODASDSUPPORT )
  {
    npUCB->Flags |= UCBF_NODASDSUPPORT;
  }

  /*--------------------------------------------------*/
  /* Scan ROM packets created by INT 13 Geometry info */
  /*--------------------------------------------------*/
  GetInt13Geometry( npAT, UnitId );

  /*--------------------------------*/
  /* Send IDENTIFY command to unit  */
  /*--------------------------------*/
  npID = (NPIDENTIFYDATA) ScratchBuf;

  npACB->TimeOut    = INIT_TIMEOUT_SHORT;
  npACB->IRQTimeOut = INIT_TIMEOUT_SHORT;

  if ( !DoIdentify( npACB, UnitId, (PBYTE) npID ) )
  {
    /*---------------------------------------------------------*/
    /* Check for SET MULTIPLE support                          */
    /*                                                         */
    /* Note: We check a Bit 15 of NumSectorsPerInt which is a  */    /*@V75103*/
    /*       Vendor-Unique bit to validate that the drive has  */    /*@V75103*/
    /*       implemented Set Multiple support properly.        */    /*@V75103*/
    /*                                                         */
    /*---------------------------------------------------------*/

    if ( npUT->Flags & UTBF_SMSENABLED )
    {
      cSec = npID->NumSectorsPerInt;

      if ( ((UCHAR) cSec) && (cSec & FX_SECPERINTVALID) )
      {
        npUCB->Flags     |= UCBF_SMSENABLED;

        cSec              = (UCHAR) cSec;
        npUCB->SecPerBlk  = (cSec < MAX_MULTMODE_BLK) ? cSec : MAX_MULTMODE_BLK;
      }
    }

    /*---------------------------------------*/
    /* Check for LBA Support Enabled         */
    /*                                       */
    /*---------------------------------------*/
    if ( npID->LBATotalSectors && npUT->Flags & UTBF_LBAMODEENABLED )
    {
      npUCB->Flags |= UCBF_LBAMODE;
    }

    /*---------------------------------------*/
    /* Get IDENTIFY Geometry of the Drive    */
    /*---------------------------------------*/
    npGEO = &npUT->IDEGeom;

    npGEO->SectorsPerTrack = npID->SectorsPerTrack;
    npGEO->TotalCylinders  = npID->TotalCylinders;
    npGEO->NumHeads        = npID->NumHeads;

    npUT->Flags |= UTBF_IDEGEOMETRYVALID;

    /*---------------------------------------*/                      /*@V75103*/
    /* Save Model/Firmware Info              */                      /*@V75103*/
    /*---------------------------------------*/                      /*@V75103*/
    strnswap( npUT->ModelNum,                                        /*@V75103*/
              npID->ModelNum,                                        /*@V75103*/
              sizeof(npUT->ModelNum)-2  );                           /*@V75103*/
                                                                     /*@V75103*/
    strnswap( npUT->FirmwareRN,                                      /*@V75103*/
              npID->FirmwareRN,                                      /*@V75103*/
              sizeof(npUT->FirmwareRN)-2 );                          /*@V75103*/
  }

  /*-------------------------------------*/
  /* Try to reconsile various reported   */
  /* geometries                          */
  /*-------------------------------------*/

  /*---------------------------------------*/
  /* User specified geometry is considered */
  /* first                                 */
  /*---------------------------------------*/
  if ( npUT->Flags & UTBF_CMDGEOMETRYVALID )
  {
    npUCB->LogGeom   = npUT->CMDLogGeom;
    npUCB->PhysGeom  = npUT->CMDPhysGeom;
    npUCB->WrtPreCmp = npUT->CMDWrtPreCmp;
  }

  /*--------------------------------------*/
  /* If INT 13 geometry available use it. */
  /*--------------------------------------*/
  else if ( npUT->Flags & UTBF_I13GEOMETRYVALID )
  {
    npUCB->LogGeom   = npUT->I13LogGeom;
    npUCB->PhysGeom  = npUT->I13PhysGeom;
    npUCB->WrtPreCmp = npUT->I13WrtPreCmp;

    /*----------------------------------------*/
    /* If the Physical Head count is > 16     */
    /* then this may be a translating BIOS    */
    /* use the IDENTIFY geometry if available */
    /*----------------------------------------*/
    if ( npUCB->PhysGeom.NumHeads > 16 || npUCB->Flags & UCBF_ONTRACK ) /*@V108555*/
    {
      if ( npUT->Flags & UTBF_IDEGEOMETRYVALID )
      {
        npUCB->PhysGeom  = npUT->IDEGeom;
        npUCB->WrtPreCmp = -1;
      }
    }
  }
  /*---------------------------------------*/
  /* We dont have a valid INT 13 Geometry  */
  /* for the drive. Use the IDENTIFY       */
  /* geometry.                             */
  /*---------------------------------------*/
  else if ( npUT->Flags & UTBF_IDEGEOMETRYVALID )
  {
    npUCB->LogGeom   = npUT->IDEGeom;
    npUCB->PhysGeom  = npUT->IDEGeom;
    npUCB->WrtPreCmp = -1;
  }

  /*-----------------------------------------*/
  /* We have no valid geometry for the drive */
  /* skip to next controller.                */
  /*-----------------------------------------*/
  else
  {
    npUT->Status = UTS_NOT_PRESENT;
  }

  /*-----------------------------------------*/
  /* Validate Physical Geometry              */
  /*                                         */
  /*-----------------------------------------*/

  if ( (npUCB->PhysGeom.NumHeads > 16) || !npUCB->PhysGeom.NumHeads )/*@V88700*/
  {
    npUT->Status = UTS_NOT_PRESENT;
  }

  if ( !npUT->Status )
  {
    /*----------------------------------------------------*/
    /* Verify the unit is operational by reading Sector 0 */
    /*----------------------------------------------------*/
    npACB->UnitCB[UnitId].ReqFlags |= (UCBR_RECAL | UCBR_SETPARAM);

    npACB->TimeOut    = INIT_TIMEOUT_LONG;
    npACB->IRQTimeOut = INIT_TIMEOUT_LONG;

    if ( !(rc = ReadDrive( npACB, UnitId, 0, 1, ScratchBuf )) )     /*@V108555*/
    {                                                               /*@VVVVVVV*/
      /*--------------------------------------------------*/
      /* Detect ONTRACK formatted drive                   */
      /*                                                  */
      /* Check for a valid MBR and scan partition entries */
      /* for Type 0x41 partition. If found, then user     */
      /* data starts at Sector 63 and we set physical     */
      /* drive geometry to the IDE geometry to be         */
      /* consistent with ONTRACKs XBIOS Int 13 support    */
      /*--------------------------------------------------*/

      if ( !CheckONTRACKDrive( ScratchBuf ) )
      {
        npUT->Flags     |= UTBF_ONTRACK;
        npUCB->ReqFlags |= UCBR_SETPARAM;

        CalcONTRACKGeometry( npUT, npUCB );                         /*@VAAAAAA*/
      }                                                             /*@V108555*/

      /*-----------------------------------------------------*/
      /* Some disks lie about their capacity.                */
      /*                                                     */
      /* Calculate the total capacity by considering         */
      /* geometry sources in the following order:            */
      /*                                                     */
      /* 1.) Command Line Parms                              */
      /* 2.) IDENTIFY command results                        */
      /* 3.) BIOS (INT 13) reported geometry.                */
      /*                                                     */
      /*-----------------------------------------------------*/
      if ( npUT->Flags & UTBF_CMDGEOMETRYVALID )
      {
        npGEO = &npUCB->PhysGeom;
      }
      else if ( npUT->Flags & UTBF_IDEGEOMETRYVALID )
      {
        npGEO = &npUT->IDEGeom;
      }
      else
      {
        npGEO = &npUCB->PhysGeom;
      }

      rc=VerifyEndOfMedia( npAT, UnitId, npGEO, (PULONG) &MaxSec );

      /*---------------------------------------------------*/
      /* On some ESDI controllers, the IDENTIFY geometry   */
      /* does not take into account sectors lost due       */
      /* to 'Sector Sparing'.                              */
      /*                                                   */
      /* Try again using the BIOS or Command Line geometry */
      /*---------------------------------------------------*/

      if ( rc && (npGEO == &npUT->IDEGeom) )
      {
        npGEO = &npUCB->PhysGeom;
        rc=VerifyEndOfMedia( npAT, UnitId, npGEO, (PULONG) &MaxSec );
      }

    }

    if ( !rc )
    {
      /*-----------------------------------------------*/           /*@V108555*/
      /* The user data area on ONTRACK formatted disks */           /*@VVVVVVV*/
      /* starts at Sector 63. Make the reported disk   */
      /* capacity smaller by this factor.              */
      /*                                               */
      /* Also set the ONTRACK flag in the UCB so that  */
      /* the State Machine now adds this offset to     */
      /* all IORB requests.                            */
      /*-----------------------------------------------*/
      if ( npUT->Flags & UTBF_ONTRACK )
      {
        MaxSec       -= ONTRACK_SECTOR_OFFSET;
        npUCB->Flags |= UCBF_ONTRACK;                                /*@VAAAAAA*/
      }                                                              /*@V108555*/

      /*----------------------------------------------------*/
      /* Adjust the cylinder count in the physical geometry */
      /* to the last full cylinder.                         */
      /*----------------------------------------------------*/
      npGEO = &npUCB->PhysGeom;

      npGEO->TotalSectors   = MaxSec;
      SecPerCyl = npGEO->SectorsPerTrack * npGEO->NumHeads;
      npGEO->TotalCylinders = MaxSec / SecPerCyl;
      npGEO->BytesPerSector = 512;

      /*----------------------------------------------------*/
      /* Adjust the cylinder count in the logical geometry  */
      /* to reflect the full capacity of the disk.          */
      /*----------------------------------------------------*/
      npGEO = &npUCB->LogGeom;

      npGEO->TotalSectors   = MaxSec;
      SecPerCyl = npGEO->SectorsPerTrack * npGEO->NumHeads;
      npGEO->TotalCylinders = MaxSec / SecPerCyl;
      npGEO->BytesPerSector = 512;

      /*----------------------------------------------------*/
      /* Some BIOS types hold back the last reported        */
      /* cylinder. Make our counts consistent if the        */
      /* BIOS is reporting 1 less logical cylinder than     */
      /* we expect.                                         */
      /*----------------------------------------------------*/
      if ( (npUT->Flags & UTBF_I13GEOMETRYVALID) &&
                                   !(npUT->Flags & UTBF_CMDGEOMETRYVALID) )
      {
        if ((npGEO->TotalCylinders - npUT->I13LogGeom.TotalCylinders) == 1 )
        {
          npGEO->TotalCylinders -= 1;
          npGEO->TotalSectors   -= SecPerCyl;
        }
      }
    }
    else
    {
      npUT->Status = UTS_READ_0_FAILED;
    }
  }
  return ( npUT->Status );

}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT NEAR VerifyEndOfMedia( NPATBL npAT, USHORT UnitId, NPGEOMETRY npGEOC,
                                                                PULONG MaxSec )
{
  ULONG         c;
  ULONG         r;
  ULONG         MaxLBA;
  ULONG         MinLBA;
  USHORT        rc    = 1;
  NPACB         npACB = npAT->npACB;
  NPUCB         npUCB = &npACB->UnitCB[UnitId];
  NPUTBL        npUT  = &npAT->Unit[UnitId];
  NPGEOMETRY    npGEO;


  /*-----------------------------------------------------*/
  /* Some disks lie about their capacity. Search for the */
  /* last readable cylinder.                             */
  /*-----------------------------------------------------*/

  npACB->TimeOut = INIT_TIMEOUT_LONG;

  MaxLBA = npGEOC->TotalCylinders  *
           npGEOC->NumHeads        *
           npGEOC->SectorsPerTrack ;

  npGEO = &npUCB->PhysGeom;

  c = npGEO->NumHeads * npGEO->SectorsPerTrack;

  MaxLBA = ( MaxLBA / c ) * c;

  MinLBA = MaxLBA - c * 10;

  for ( r = MaxLBA; r > MinLBA; r -= c )
  {
    if ( !(rc = ReadDrive( npACB, UnitId, r-1, 0, NULL )) )         /*@V108555*/
    {
      break;
    }
  }

  *MaxSec = ( !rc ) ? r : 0;

  return ( rc );
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT ReadDrive( NPACB  npACB, USHORT UnitId, ULONG RBA,           /*@V108555*/
                  USHORT Retry, PBYTE pBuf )                        /*@V108555*/
{
  NPIORB_EXECUTEIO      npTI = &InitIORB;

  setmem( (PBYTE) npTI, 0, sizeof(IORB_EXECUTEIO) );

  if ( !pBuf )                                                      /*@V108555*/
  {                                                                 /*@VVVVVVV*/
    npTI->iorbh.CommandModifier = IOCM_READ_VERIFY;
    npTI->cSGList               = 0;
    npTI->pSGList               = 0;
  }
  else
  {
    npTI->iorbh.CommandModifier = IOCM_READ;

    if ( DevHelp_VirtToPhys( (PVOID)  pBuf,
                             (PULONG) &ScratchSGList.ppXferBuf ) )
    {
      _asm { int 3 }
    }
    npTI->cSGList               = 1;
    npTI->pSGList               = &ScratchSGList;                   /*@VAAAAAA*/
  }                                                                 /*@V108555*/

  npTI->iorbh.Length          = sizeof(IORB_EXECUTEIO);
  npTI->iorbh.UnitHandle      = (USHORT) &npACB->UnitCB[UnitId];
  npTI->iorbh.CommandCode     = IOCC_EXECUTE_IO;
  npTI->iorbh.RequestControl  = IORB_ASYNC_POST | ((Retry) ? 0 : IORB_DISABLE_RETRY);
  npTI->iorbh.NotifyAddress   = NotifyIORBDone;
  npTI->RBA                   = RBA;
  npTI->BlockCount            = 1;
  npTI->BlocksXferred         = 0;
  npTI->BlockSize             = 512;

  ADDEntryPoint( (PIORB) npTI );

  DISABLE
  while( !(npTI->iorbh.Status & IORB_DONE) )
  {

    DevHelp_ProcBlock( (ULONG)(PIORB) npTI, -1, WAIT_IS_NOT_INTERRUPTABLE );
    DISABLE
  }
  ENABLE

  return( (npTI->iorbh.Status & IORB_ERROR) ? npTI->iorbh.ErrorCode : 0 );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT DoIdentify( NPACB npACB, USHORT UnitId, PBYTE pID )
{
  NPIORB_EXECUTEIO      npTI  = &InitIORB;
  NPUCB                 npUCB = &npACB->UnitCB[UnitId];
  USHORT                ErrorCode = 0;

  if ( ( npACB->UnitCB[0].Flags & UCBF_IDENTIFYFAIL ) &&
       !(npACB->UnitCB[0].Flags & UCBF_ATAPI_DEVICE ) )            /*@V87325*/
  {
    npUCB->Flags |= UCBF_IDENTIFYFAIL;
    ErrorCode     = IOERR_DEVICE_REQ_NOT_SUPPORTED;

    goto DoIdentify_Exit;
  }

  npUCB->ReqFlags |= ACBR_IDENTIFY;

  if ( DevHelp_VirtToPhys( (PVOID)  pID,
                           (PULONG) &ScratchSGList.ppXferBuf ) )    /*@V108555*/
  {
    _asm { int 3 }
  }

  setmem( (PBYTE) npTI, 0, sizeof(IORB_EXECUTEIO) );

  npTI->iorbh.Length          = sizeof(IORB_EXECUTEIO);
  npTI->iorbh.UnitHandle      = (USHORT) &npACB->UnitCB[UnitId];
  npTI->iorbh.CommandCode     = IOCC_EXECUTE_IO;
  npTI->iorbh.CommandModifier = IOCM_NO_OPERATION;
  npTI->iorbh.RequestControl  = IORB_ASYNC_POST | IORB_DISABLE_RETRY;
  npTI->iorbh.NotifyAddress   = NotifyIORBDone;
  npTI->RBA                   = 0;
  npTI->cSGList               = 1;
  npTI->pSGList               = &ScratchSGList;                     /*@V108555*/
  npTI->BlockCount            = 1;
  npTI->BlocksXferred         = 0;
  npTI->BlockSize             = 512;

  ADDEntryPoint( (PIORB) npTI );

  DISABLE
  while( !(npTI->iorbh.Status & IORB_DONE) )
  {

    DevHelp_ProcBlock( (ULONG)(PIORB) npTI, -1, WAIT_IS_NOT_INTERRUPTABLE );
    DISABLE
  }
  ENABLE

  npUCB->ReqFlags &= ~ACBR_IDENTIFY;

  if ( npTI->iorbh.Status & IORB_ERROR )
  {
    npUCB->Flags |= UCBF_IDENTIFYFAIL;
    ErrorCode = npTI->iorbh.ErrorCode;

    if ( !(npACB->Flags & ACBF_DISABLERESET) )
    {
      ResetController( npACB );
    }
  }

DoIdentify_Exit:

  return( ErrorCode );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID NEAR ResetController( NPACB npACB )
{
  NPIORB_EXECUTEIO      npTI = &InitIORB;

  npACB->UnitCB[0].ReqFlags |= ACBR_RESETCONTROLLER;

  setmem( (PBYTE) npTI, 0, sizeof(IORB_EXECUTEIO) );

  npTI->iorbh.Length          = sizeof(IORB_EXECUTEIO);
  npTI->iorbh.UnitHandle      = (USHORT) &npACB->UnitCB[0];
  npTI->iorbh.CommandCode     = IOCC_EXECUTE_IO;
  npTI->iorbh.CommandModifier = IOCM_NO_OPERATION;
  npTI->iorbh.RequestControl  = IORB_ASYNC_POST | IORB_DISABLE_RETRY;
  npTI->iorbh.NotifyAddress   = NotifyIORBDone;

  ADDEntryPoint( (PIORB) npTI );

  DISABLE
  while( !(npTI->iorbh.Status & IORB_DONE) )
  {

    DevHelp_ProcBlock( (ULONG)(PIORB) npTI, -1, WAIT_IS_NOT_INTERRUPTABLE );
    DISABLE
  }
  ENABLE

  npACB->UnitCB[0].ReqFlags &= ~ACBR_RESETCONTROLLER;

  return;
}
/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID GetInt13Geometry( NPATBL npAT, USHORT UnitId )
{
  PROMCFG       pROMCFG;
  NPUTBL        npUT;
  NPGEOMETRY    npGEO;
  USHORT        DriveId;
  USHORT        i;

  npUT = &npAT->Unit[UnitId];

  pROMCFG = (PROMCFG) MAKEP(SELECTOROF(pDDD_Parm_List),
                              (USHORT) pDDD_Parm_List->disk_config_table);

  DriveId = npUT->DriveId;

  i =0;
  while ( OFFSETOF(pROMCFG) )
  {
    if ( (pROMCFG->ROMdevnbr == DriveId) && (pROMCFG->ROMflags & ROMfixed) )
    {
      npUT->Flags |= UTBF_I13GEOMETRYVALID;
      npGEO = &npUT->I13LogGeom;

      npGEO->TotalCylinders  = pROMCFG->ROMcyls;
      npGEO->SectorsPerTrack = pROMCFG->ROMsecptrk;
      npGEO->NumHeads        = pROMCFG->ROMheads;


      npUT->I13PhysGeom = *npGEO;
      break;
    }

    OFFSETOF(pROMCFG) = pROMCFG->ROMlink;
  }

  if ( OFFSETOF(pROMCFG) )
  {
    GetFixedTableGeometry( npAT, UnitId );
  }
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID FAR NotifyIORBDone( PIORB pIORB )
{
  USHORT        AwakeCount;

  if ( pIORB->Status & IORB_DONE )
  {
    DevHelp_ProcRun( (ULONG) pIORB, &AwakeCount );
  }
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID GetFixedTableGeometry( NPATBL npAT, USHORT UnitId )
{
  PFDPARMTABLE_XLATE    pFDT;
  PBYTE                 pXBIOSId;                                   /*@V108555*/

  ULONG                 ppInt4xBase;                                /*@V108555*/
  ULONG                 ppFDT;
  ULONG                 ppXBIOSId;                                  /*@V108555*/

  PULONG                BIOSVec;

  NPUTBL                npUT;
  NPGEOMETRY            npGEO;

  USHORT                ModeFlag;
  USHORT                DriveId;
  USHORT                i;

  UCHAR                 CkSum;
  UCHAR                 Reserved1;


  npUT = &npAT->Unit[UnitId];

  DriveId = npUT->DriveId;

  if ( DriveId >= 0x80 && DriveId <= 0x81
                               && (npUT->Flags & UTBF_I13GEOMETRYVALID) )
  {
    if ( !DevHelp_PhysToVirt((ULONG)   ((DriveId == 0x80) ? FDTAB_0 : FDTAB_1),
                             (USHORT)  sizeof(ULONG),
                             (PVOID)   &BIOSVec,
                             (PUSHORT) &ModeFlag          ) )
    {
      ppInt4xBase = (((ULONG) SELECTOROF(*BIOSVec)) << 4);          /*@V108555*/

      ppFDT = ppInt4xBase + OFFSETOF(*BIOSVec);                     /*@V108555*/

      if ( !DevHelp_PhysToVirt((ULONG)   ppFDT,
                               (USHORT)  sizeof(FDPARMTABLE_XLATE),
                               (PVOID)   &pFDT,
                               (PUSHORT) &ModeFlag    ) )
      {
        npGEO = &npUT->I13PhysGeom;

        if ( pFDT->FDMaxHdX == npGEO->NumHeads &&
                           pFDT->FDSecTrkX == npGEO->SectorsPerTrack )
        {
          npUT->I13WrtPreCmp = pFDT->FDWritePCompX;

          if ( pFDT->FDMaxCylX > 1024 )
          {
            npUT->I13LogGeom.TotalCylinders  = pFDT->FDMaxCylX;
            npUT->I13PhysGeom.TotalCylinders = pFDT->FDMaxCylX;
          }
        }

        if ( (pFDT->FDtranslate & 0x00F0) == TRANSLATE_A0 )
        {
          for ( i = CkSum = 0;
                i < sizeof(FDPARMTABLE_XLATE);
                i++  )
          {
            CkSum += *((PBYTE) pFDT + i);
          }

          if ( !CkSum )
          {
            npGEO->NumHeads        = pFDT->FDpheads;
            npGEO->TotalCylinders  = pFDT->FDpcyls;
            npGEO->SectorsPerTrack = pFDT->FDpsectors;

          }
        }
      }

      /*-----------------------------------------------*/           /*@V108555*/
      /* Check for ONTRACK XBIOS Int 13 Support        */           /*@VVVVVVV*/
      /*                                               */
      /* XBIOS redirects INT 41 to point to its data   */
      /* area. Check at a fixed offset from the Int 41 */
      /* selector for the XBIOS identification string. */
      /*-----------------------------------------------*/
      if ( DriveId == 0x80 )
      {
        ppXBIOSId = ppInt4xBase + XBIOS_ID_STRING_OFFSET;

        if ( !DevHelp_PhysToVirt((ULONG)   ppXBIOSId,
                                 (USHORT)  XBIOSIdLength,
                                 (PVOID)   &pXBIOSId,
                                 (PUSHORT) &ModeFlag    ) )
        {
          if ( !MemCmp( pXBIOSId, XBIOSIdString, XBIOSIdLength ) )
          {
            XBIOSPresent = 1;
          }
        }                                                           /*@VAAAAAA*/
      }                                                             /*@V108555*/
    }
  }
}



/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT NEAR ScanForOtherADDs()
{
  NPIORB_CONFIGURATION  pIORB;
  DEVICETABLE           *pDeviceTable;
  NPADAPTERINFO         pAdapterInfo;

  struct DevClassTableStruc far *pDriverTable;

  USHORT                rc, i, j, k, n;

  USHORT                UnitFlags;
  USHORT                UnitType;
  USHORT                DriveCount = 0;

  VOID                  (FAR * DriverEP) ();

  if ( rc = DevHelp_GetDOSVar((USHORT) DHGETDOSV_DEVICECLASSTABLE, 1,
                              (PPVOID) &pDriverTable) )
  {
    _asm { int 3 }
  }

  pDeviceTable = (DEVICETABLE *) ScratchBuf;

  for (i = 0; i < pDriverTable->DCCount; i++)
  {
    pIORB = (NPIORB_CONFIGURATION) &InitIORB;
    pIORB->iorbh.Length = sizeof(IORB_CONFIGURATION);
    pIORB->iorbh.CommandCode = IOCC_CONFIGURATION;
    pIORB->iorbh.CommandModifier = IOCM_GET_DEVICE_TABLE;
    pIORB->iorbh.Status = 0;
    pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
    pIORB->iorbh.NotifyAddress = &InitPost;
    pIORB->pDeviceTable = (PVOID) ScratchBuf;
    pIORB->DeviceTableLen = sizeof(ScratchBuf);

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

    setmem( ScratchBuf, 0, sizeof(ScratchBuf) );

    (*DriverEP) ((PVOID)(pIORB));

    while ( !(pIORB->iorbh.Status & IORB_DONE) )  /* Wait till done */
    ;

    if (pIORB->iorbh.Status & IORB_ERROR )
      continue;

    for (j = 0; j < pDeviceTable->TotalAdapters; j++)
    {
      pAdapterInfo =  pDeviceTable->pAdapter[j];

      for (k = 0; k < pAdapterInfo->AdapterUnits; k++)
      {
        UnitFlags = pAdapterInfo->UnitInfo[k].UnitFlags;
        UnitType  = pAdapterInfo->UnitInfo[k].UnitType;
        if ( !(UnitFlags & (UF_NODASD_SUPT | UF_DEFECTIVE)) )
        {
          if ( (UnitType == UIB_TYPE_DISK) && !(UnitFlags & UF_REMOVABLE) )
          {
            DriveCount++;
          }
        }
      }
    }
  }

  return (DriveCount);
}

VOID FAR InitPost(PIORB pIORB)
{
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID NEAR CheckACBViable( NPATBL npAT )                              /*@V90963*/
{                                                                    /*@VVVVVV*/
  NPACB         npACB;

  if ( npACB = npAT->npACB )
  {
    if ( !npAT->cUnits )
    {
      DeactivateACB( npACB );
      DeallocAdapterResources( npAT );                              /*@V108555*/
      npACBPool--;
      cAdapters--;

      npAT->npACB  = ACBPtrs[cAdapters].npACB = 0;
      npAT->cUnits = 0;
    }
  }                                                                  /*@VAAAAA*/
}                                                                    /*@V90963*/

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

#define PCF_ADAPTER0    0x8000
#define PCF_ADAPTER1    0x4000
#define PCF_ADAPTER2    0x2000                                     /*@V87325*/
#define PCF_ADAPTER3    0x1000                                     /*@V87325*/

#define PCF_IGNORE      0x0800
#define PCF_RESET       0x0400
#define PCF_PORT        0x0200
#define PCF_IRQ         0x0100
#define PCF_DM          0x0080

#define PCF_UNIT0       0x0040
#define PCF_UNIT1       0x0020
#define PCF_GEO         0x0010
#define PCF_TIMEOUT     0x0008
#define PCF_LBAMODE     0x0004
#define PCF_SMS         0x0002
#define PCF_ATAPI       0x0001                                       /*@V93531*/

#define PCF_CLEAR_ADAPTER (PCF_IGNORE | PCF_IRQ  | PCF_PORT | PCF_DM |    \
                           PCF_RESET | PCF_UNIT0  | PCF_UNIT1 )

#define PCF_CLEAR_UNIT (PCF_GEO | PCF_TIMEOUT | PCF_DM | PCF_LBAMODE |    \
                        PCF_SMS | PCF_ATAPI )

USHORT ParseCmdLine( PDDD_PARM_LIST pADDParms, NPATBL npAT )
{
  PSZ           pCmdLine;
  CC            cc;

  NPUTBL        npUT;
  NPGEOMETRY    npGEO;
  NPSZ          pOutBuf;


  USHORT        Flags = 0;
  USHORT        Mask;

  USHORT        i,j;
  USHORT        rc = 0;
  USHORT        Length;

  USHORT        AdptId;
  USHORT        UnitId;

  USHORT        SecPerTrk;
  USHORT        TotalCyl;
  USHORT        NumHeads;
  USHORT        WrtPreCmp;


  if ( !CheckSMSEnable() )                                          /*@V108555*/
  {                                                                 /*@V108555*/
    for (i=0;i<MAX_ADAPTERS ;i++ )                                   /*@V87325*/
    {                                                                /*@V87325*/
       for (j=0;j<MAX_UNITS ;j++ )                                   /*@V87325*/
       {                                                             /*@V87325*/
         npAT[i].Unit[j].Flags = UTBF_SMSENABLED;                    /*@V87325*/
       }                                                             /*@V87325*/
    }                                                                /*@V87325*/
  }                                                                 /*@V108555*/

  pOutBuf = poutbuf;

  pCmdLine = MAKEP( SELECTOROF(pADDParms),
                    (USHORT)   pADDParms->cmd_line_args );

  cc = Command_Parser( (PSZ)          pCmdLine,
                       (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] )
      {

        /*-------------------------------------------*/
        /* Quite/Verbose Mode - /!V /V               */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_V:

          Verbose = 1;
          break;

        case TOK_ID_NOT_V:
          Verbose = 0;
          break;


        /*-------------------------------------------*/
        /* Adapter Id - /A:0, /A:1, /A:2 or /A:3     */            /*@V87325*/
        /*                                           */
        /* Check for  correct adapter index. Address */
        /* ADAPTERTABLE structure.                   */
        /*-------------------------------------------*/

        case TOK_ID_ADAPTER:

          AdptId = pOutBuf[2];

          switch (AdptId)                                          /*@V87325*/
          {                                                        /*@V87325*/
             case  0 : Mask = PCF_ADAPTER0; break;                 /*@V87325*/
             case  1 : Mask = PCF_ADAPTER1; break;                 /*@V87325*/
             case  2 : Mask = PCF_ADAPTER2; break;                 /*@V87325*/
             case  3 : Mask = PCF_ADAPTER3;                        /*@V87325*/
          }                                                        /*@V87325*/

          if ( (AdptId > (MAX_ADAPTERS-1)) || (Flags & Mask))
          {
            rc = 1;
            break;
          }
          Flags |= Mask;
          Flags &= ~PCF_CLEAR_ADAPTER;
          npAT = npAT+AdptId;

          break;

        /*-------------------------------------------*/
        /* Base Port - /P:hhhh                       */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_PORT:

          if ( (Flags & PCF_PORT) )
          {
            rc = 1;
            break;
          }
          npAT->BasePort = *(NPUSHORT)(pOutBuf+2);
          Flags |= PCF_PORT;

          npAT->Flags &= ~ATBF_DISABLED;                           /*@V87325*/

          break;


        /*-------------------------------------------*/
        /* IRQ Level - /IRQ:nn                       */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_IRQ:

          if ( (Flags & PCF_IRQ) )
          {
            rc = 1;
            break;
          }
          npAT->IRQLevel = pOutBuf[2];
          Flags |= PCF_IRQ;                                          /*@V64402*/

          break;

        /*-------------------------------------------*/
        /* Ignore Adapter - /I                       */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_IGNORE:

          if ( (Flags & PCF_IGNORE) )
          {
            rc = 1;
            break;
          }

          Flags |= PCF_IGNORE;

          npAT->Flags |= ATBF_DISABLED;

          break;


        /*-------------------------------------------*/
        /* Allow/Prevent Adapter Resets - /R, /!R    */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_RESET:

          if ( Flags & PCF_RESET )
          {
            rc = 1;
            break;
          }
          Flags |= PCF_RESET;
          break;

        case TOK_ID_NORESET:

          if ( Flags & PCF_RESET )
          {
            rc = 1;
            break;
          }
          Flags |= PCF_RESET;

          npAT->Flags |= ATBF_DISABLERESET;
          break;


        /*-------------------------------------------*/
        /* DASD Manager Support - /!DM, /DM          */
        /*                                           */
        /* If this is specified before /U (Unit Id)  */
        /* then this switch applies to all units.    */
        /* Otherwise, this switch applies to the     */
        /* current unit.                             */
        /*-------------------------------------------*/

        case TOK_ID_NOT_DM:

          if ( (Flags & PCF_DM) )
          {
            rc = 1;
            break;
          }
          Flags |= PCF_DM;                                           /*@V64402*/

          if ( Flags & (PCF_UNIT0 | PCF_UNIT1) )
          {
            npUT->Flags |= UTBF_NODASDSUPPORT;
          }
          else
          {
            for (i = 0; i < MAX_UNITS; i++ )
            {
              npAT->Unit[i].Flags |= UTBF_NODASDSUPPORT;
            }
          }
          break;

        case TOK_ID_DM:

          if ( (Flags & PCF_DM) )
          {
            rc = 1;
            break;
          }
          Flags |= PCF_DM;                                           /*@V64402*/

          if ( Flags & (PCF_UNIT0 | PCF_UNIT1) )
          {
            npUT->Flags &= ~UTBF_NODASDSUPPORT;
          }

          break;

        /*-------------------------------------------*/
        /* Unit Number - /U:0, /U:1                  */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_UNIT:

          UnitId = pOutBuf[2];
          Mask = (!UnitId) ? PCF_UNIT0 : PCF_UNIT1;
          if ( (UnitId > (MAX_UNITS-1)) || (Flags & Mask))
          {
            rc = 1;
            break;
          }
          Flags &= ~(PCF_CLEAR_UNIT);
          Flags |= Mask;
          npUT   = &npAT->Unit[UnitId];

          break;

        /*-------------------------------------------*/
        /* Unit Geometry -  /G:(C,H,S,W) or /G:nn    */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_GEO:

          if ( (Flags & PCF_GEO) )
          {
            rc = 1;
            break;
          }
          Flags |= PCF_GEO;                                          /*@V64402*/

          /*-------------------------------------------------*/
          /* User specified a Physical Geometry for the      */
          /* disk.                                           */
          /*                                                 */
          /* Check if DriveType or (Cyl,Head,Sec,WrtPCmp)    */
          /* was provided.                                   */
          /*-------------------------------------------------*/

          if ( (pOutBuf[2] >> 4) == GEOTYPE_DRIVETYPE )
          {
            if ( DriveTypeToGeometry( (USHORT)     pOutBuf[3],
                                      (NPGEOMETRY) &npUT->CMDPhysGeom,
                                      (NPUSHORT)   &npUT->CMDWrtPreCmp ) )
            {
              rc = 1;
              break;
            }
          }
          else if ( (pOutBuf[2] >> 4) == GEOTYPE_DRIVESPEC )
          {
            TotalCyl  = *(NPUSHORT) (pOutBuf+3);
            NumHeads  = *(NPUSHORT) (pOutBuf+5);
            SecPerTrk = *(NPUSHORT) (pOutBuf+7);
            WrtPreCmp = *(NPUSHORT) (pOutBuf+9);

            /*------------------------------------------*/
            /* Check if user provided physical geometry */
            /* is within reasonable limits.             */
            /*------------------------------------------*/

            if ( !NumHeads || !SecPerTrk || !TotalCyl ||
                                   SecPerTrk > 256 || NumHeads > 16 )
            {
              rc = 1;
              break;
            }

            if ( WrtPreCmp > TotalCyl )
            {
              WrtPreCmp = -1;
            }

            npUT->CMDPhysGeom.TotalCylinders  = TotalCyl;
            npUT->CMDPhysGeom.NumHeads        = NumHeads;
            npUT->CMDPhysGeom.SectorsPerTrack = SecPerTrk;
            npUT->CMDWrtPreCmp                = WrtPreCmp;
          }
          else
          {
            rc = 1;
            break;
          }

          /*-------------------------------------------------*/
          /* User specified a Logical (INT 13) for the disk. */
          /*                                                 */
          /* This is unusual since most BIOS ROMs map the    */
          /* drive Physical = Logical with translation being */
          /* done being by the controller itself.            */
          /*                                                 */
          /* Check if DriveType or (Cyl,Head,Sec,WrtPCmp)    */
          /* was provided.                                   */
          /*-------------------------------------------------*/

          if ( (pOutBuf[2] & 0x0f) == GEOTYPE_DRIVETYPE )
          {
            if ( DriveTypeToGeometry( (USHORT)     pOutBuf[3],
                                      (NPGEOMETRY) &npUT->CMDLogGeom,
                                      (NPUSHORT)   0               ) )
            {
              rc = 1;
              break;
            }
          }
          else if ( (pOutBuf[2] & 0x0f) == GEOTYPE_DRIVESPEC )
          {
            /*------------------------------------------*/
            /* Check if user provided logical geometry  */
            /* is within reasonable limits.             */
            /*------------------------------------------*/
            TotalCyl  = *(NPUSHORT) (pOutBuf+11);
            NumHeads  = *(NPUSHORT) (pOutBuf+13);
            SecPerTrk = *(NPUSHORT) (pOutBuf+15);

            if ( !NumHeads || !SecPerTrk || !TotalCyl ||
                                   SecPerTrk > 63 || NumHeads > 256 )
            {
              rc = 1;
              break;
            }

            npUT->CMDLogGeom.TotalCylinders  = TotalCyl;
            npUT->CMDLogGeom.NumHeads        = NumHeads;
            npUT->CMDLogGeom.SectorsPerTrack = SecPerTrk;
          }
          /*---------------------------------------*/
          /* If no logical geometry was provided,  */
          /* Then map the disk Logical = Physical  */
          /*---------------------------------------*/
          else
          {
            npUT->CMDLogGeom = npUT->CMDPhysGeom;
          }

          npUT->Flags |= UTBF_CMDGEOMETRYVALID;

          break;


        /*-------------------------------------------*/
        /* Unit TimeOut -  /T:nn (sec)               */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_TIMEOUT:

          if ( (Flags & PCF_TIMEOUT) )
          {
            rc = 1;
            break;
          }

          npUT->TimeOut = *(NPUSHORT) pOutBuf+2;

          Flags |= PCF_TIMEOUT;

          break;


        /*-------------------------------------------*/
        /* Enable LBA Mode - LBA                     */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_LBAMODE:

          if ( (Flags & PCF_LBAMODE) )
          {
            rc = 1;
            break;
          }

          Flags |= PCF_LBAMODE;

          npUT->Flags |= UTBF_LBAMODEENABLED;

          break;

        /*-------------------------------------------*/
        /* Enable Set Multiple Support               */
        /*                                           */
        /*-------------------------------------------*/

        case TOK_ID_SMS:

          if ( (Flags & PCF_SMS) )
          {
            rc = 1;
            break;
          }

          Flags |= PCF_SMS;

          npUT->Flags |= UTBF_SMSENABLED;

          break;


        case TOK_ID_NOT_SMS:

          if ( (Flags & PCF_SMS) )
          {
            rc = 1;
            break;
          }

          Flags |= PCF_SMS;

          npUT->Flags &= ~UTBF_SMSENABLED;                           /*@V77133*/

          break;

        /*-------------------------------------------*/              /*@V93531*/
        /* Unit is an ATAPI device                   */              /*@V93531*/
        /*                                           */              /*@V93531*/
        /*-------------------------------------------*/              /*@V93531*/
                                                                     /*@V93531*/
        case TOK_ID_ATAPI:                                           /*@V93531*/
                                                                     /*@V93531*/
          if ( (Flags & PCF_ATAPI) )                                 /*@V93531*/
          {                                                          /*@V93531*/
            rc = 1;                                                  /*@V93531*/
            break;                                                   /*@V93531*/
          }                                                          /*@V93531*/
                                                                     /*@V93531*/
          Flags |= PCF_ATAPI;                                        /*@V93531*/
                                                                     /*@V93531*/
          npUT->Flags |= UTBF_ATAPIDEVICE;                           /*@V93531*/
          npAT->Flags |= ATBF_ATAPIPRESENT;                          /*@V93531*/
                                                                     /*@V93531*/
          break;                                                     /*@V93531*/
                                                                     /*@V93531*/
                                                                     /*@V93531*/
        case TOK_ID_NOT_ATAPI:                                       /*@V93531*/
                                                                     /*@V93531*/
          if ( (Flags & PCF_ATAPI) )                                 /*@V93531*/
          {                                                          /*@V93531*/
            rc = 1;                                                  /*@V93531*/
            break;                                                   /*@V93531*/
          }                                                          /*@V93531*/
                                                                     /*@V93531*/
          Flags |= PCF_ATAPI;                                        /*@V93531*/
                                                                     /*@V93531*/
          npUT->Flags &= ~UTBF_ATAPIDEVICE;                          /*@V93531*/
                                                                     /*@V93531*/
          break;                                                     /*@V93531*/

        /*-------------------------------------------*/
        /* Unknown Switch                            */
        /*                                           */
        /*-------------------------------------------*/

        default:

          rc = 1;
          break;
      }

      if ( !rc ) pOutBuf += Length;

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

  return( rc );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT DriveTypeToGeometry( USHORT DriveType, NPGEOMETRY npGEO, NPUSHORT npWrtPreCmp )
{
  USHORT  rc = 0;

  if ( DriveType < MAX_DRIVE_TYPES && DriveTypeTable[DriveType].Sec != -1 )
  {
    npGEO->TotalCylinders  = DriveTypeTable[DriveType].Cyl;
    npGEO->NumHeads        = DriveTypeTable[DriveType].Head;
    npGEO->SectorsPerTrack = DriveTypeTable[DriveType].Sec;

    if ( npWrtPreCmp )
    {
      *npWrtPreCmp = DriveTypeTable[DriveType].WrtPreCmp;
    }
  }
  else
  {
    rc = 1;
  }

  return ( rc );
}

/*------------------------------------*/                            /*@V108555*/
/*                                    */                            /*@VVVVVVV*/
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT CheckONTRACKDrive( PBYTE pBootSec )
{
  PMBRENTRY     pMBR;
  USHORT        i;
  USHORT        rc = 1;

  pMBR = (PMBRENTRY) &pBootSec[MBR_OFFSET];

  if ( ((PUSHORT)pBootSec)[255] == 0xAA55 )
  {
    for ( i=0; i < MAX_MBR_ENTRY; i++, pMBR++ )
    {
      if ( pMBR->PartType == PARTTYPE_ONTRACK )
      {
        rc = 0;
        break;
      }
    }
  }

  return( rc );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT CalcONTRACKGeometry( NPUTBL npUT, NPUCB npUCB )
{
  NPGEOMETRY    npGEO;

  /*-------------------------------------------*/
  /* If the IDE Identify geometry is available */
  /*-------------------------------------------*/
  if ( npUT->Flags & UTBF_IDEGEOMETRYVALID )
  {
    /*------------------------------------------------*/
    /* Set the Physical Drive geometry to the default */
    /* Identify geometry.                             */
    /*------------------------------------------------*/
    npUCB->PhysGeom = npUT->IDEGeom;

    /*---------------------------------------------------*/
    /* Check if the ONTRACK XBios extension is installed */
    /*                                                   */
    /* The XBIOS exensions will not be present if the    */
    /* user did not install ONTRACK on Drive 0.          */
    /*                                                   */
    /* If XBIOS is installed, then it will have set the  */
    /* Int 13 Logical Geometry properly. Otherwise we    */
    /* calculate the Int 13 Logical Geometry here.       */
    /*                                                   */
    /* See: GetInt13Geometry for XBIOS presence check    */
    /*---------------------------------------------------*/
    if ( !XBIOSPresent )
    {
      npUCB->LogGeom = npUCB->PhysGeom;

      npGEO = &npUCB->LogGeom;

      /*-----------------------------------------------*/
      /* Double the number of heads until the cylinder */
      /* count drops below 1024.                       */
      /*-----------------------------------------------*/
      while( npGEO->TotalCylinders > MAX_INT13_CYL+1 )
      {
        npGEO->NumHeads       <<= 1;
        npGEO->TotalCylinders >>= 1;
      }
    }

    npUT->I13PhysGeom = npUCB->PhysGeom;
  }

  return ( 0 );                                                     /*@VAAAAAA*/
}                                                                   /*@V108555*/


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

VOID PrintInfo( NPATBL npAT )
{
  NPACB    npACB;
  NPUCB    npUCB;
  NPUTBL   npUT;

  USHORT   i;
  USHORT   j;

  UCHAR    Buf[122];
  PUCHAR   s = Buf;

  TTYWrite( VersionMsg );

  for ( j = 0; j < MAX_ADAPTERS; j++, npAT++ )
  {
    if ( !(npAT->Flags & ATBF_DISABLED) )
    {
      npACB = npAT->npACB;

      sprntf( (PUCHAR) s,
              (PUCHAR) VControllerInfo,                             /*@V106915*/
              (USHORT) j,
              (USHORT) npAT->BasePort,
              (USHORT) npAT->IRQLevel,
              (PSZ)    AdptMsgs[npAT->Status] );

      TTYWrite( s );

      if( npACB = npAT->npACB )
      {
        for ( i=0; i < npACB->cUnits; i++ )
        {

          npUT =  &npAT->Unit[i];
          npUCB = &npACB->UnitCB[i];

          if ( npUCB->Flags & UCBF_ATAPI_DEVICE )                  /*@V87325*/
          {                                                        /*@V87325*/
            sprntf( (PUCHAR) s,                                    /*@V87325*/
                     (PUCHAR) VUnitInfo1,                         /*@V106915*/
                     (USHORT) i,                                   /*@V87325*/
                     (PSZ)    ATAPIMsg );                          /*@V87325*/
                                                                   /*@V87325*/
            TTYWrite( s );                                         /*@V87325*/
          }                                                        /*@V87325*/
          else if ( npUT->Status == UTS_NO_MASTER )               /*@V108783*/
          {                                                       /*@V108783*/
            sprntf( (PUCHAR) s,                                   /*@V108783*/
                     (PUCHAR) VUnitInfo1,                         /*@V108783*/
                     (USHORT) i,                                  /*@V108783*/
                     (PSZ)    UnitMsgs[npUT->Status]);            /*@V108783*/
                                                                  /*@V108783*/
            TTYWrite( s );                                        /*@V108783*/
          }                                                       /*@V108783*/
          else                                                    /*@V108783*/
          {
            sprntf( (PUCHAR) s,
                    (PUCHAR) VUnitInfo2,                            /*@V106915*/
                    (USHORT) i,
                    (PSZ)    UnitMsgs[npUT->Status],

                    (PSZ)   ((npUCB->Flags & UCBF_SMSENABLED) ?        /*@V75103*/
                                                 MsgSMSOn  : MsgNull), /*@V75103*/

                    (PSZ)   ((npUCB->Flags & UCBF_LBAMODE) ?
                                                 MsgLBAOn : MsgNull),

                    (PSZ)   ((npUCB->Flags & UCBF_ONTRACK) ?        /*@V108555*/
                                         MsgONTrackOn : MsgNull) ); /*@V108555*/


            TTYWrite( s );

            sprntf( (PUCHAR) s,                                        /*@V75103*/
                    (PUCHAR) VModelInfo,                              /*@V106915*/
                                                                       /*@V75103*/
                    (PSZ)    ((npUT->ModelNum[0]) ?                    /*@V75103*/
                                      npUT->ModelNum : VModelUnknown), /*@V75103*/
                                                                       /*@V75103*/
                    (PSZ)    npUT->FirmwareRN  );                      /*@V75103*/
                                                                       /*@V75103*/
            TTYWrite( s );                                             /*@V75103*/

            sprntf( (PUCHAR) s,
                    (PUCHAR) VGeomInfo1,                            /*@V106915*/
                    (USHORT) npUCB->LogGeom.TotalCylinders,
                    (USHORT) npUCB->PhysGeom.TotalCylinders,
                    (USHORT) npUT->I13LogGeom.TotalCylinders,
                    (USHORT) npUT->I13PhysGeom.TotalCylinders,
                    (USHORT) npUT->CMDLogGeom.TotalCylinders,
                    (USHORT) npUT->CMDPhysGeom.TotalCylinders,
                    (USHORT) npUT->IDEGeom.TotalCylinders       );

            TTYWrite( s );

            sprntf( (PUCHAR) s,
                    (PUCHAR) VGeomInfo2,                            /*@V106915*/
                    (USHORT) npUCB->LogGeom.NumHeads,
                    (USHORT) npUCB->PhysGeom.NumHeads,
                    (USHORT) npUT->I13LogGeom.NumHeads,
                    (USHORT) npUT->I13PhysGeom.NumHeads,
                    (USHORT) npUT->CMDLogGeom.NumHeads,
                    (USHORT) npUT->CMDPhysGeom.NumHeads,
                    (USHORT) npUT->IDEGeom.NumHeads             );

            TTYWrite( s );

            sprntf( (PUCHAR) s,
                    (PUCHAR) VGeomInfo3,                            /*@V106915*/
                    (USHORT) npUCB->LogGeom.SectorsPerTrack,
                    (USHORT) npUCB->PhysGeom.SectorsPerTrack,
                    (USHORT) npUT->I13LogGeom.SectorsPerTrack,
                    (USHORT) npUT->I13PhysGeom.SectorsPerTrack,
                    (USHORT) npUT->CMDLogGeom.SectorsPerTrack,
                    (USHORT) npUT->CMDPhysGeom.SectorsPerTrack,
                    (USHORT) npUT->IDEGeom.SectorsPerTrack      );

            TTYWrite( s );

          }
        }
      }
    }
  }

  sprntf( s, (PUCHAR) VBlankLine );                                 /*@V106915*/
  TTYWrite( s );
}

VOID TTYWrite( PSZ Buf )
{
  InitMsg.MsgStrings[0] = Buf;

  DevHelp_Save_Message( (NPBYTE) &InitMsg );
}

/****************************/                                       /*@V75103*/
/* Copy & Swap String Bytes */                                       /*@V75103*/
/****************************/                                       /*@V75103*/
                                                                     /*@V75103*/
static strnswap( PSZ d, PSZ s, USHORT n )                            /*@V75103*/
{                                                                    /*@V75103*/
  USHORT    j = 1;                                                   /*@V75103*/
                                                                     /*@V75103*/
  while( s[j] && n-- ) { *d++ = s[j]; j^=1; s+=(j*2); }              /*@V75103*/
  *d = 0;                                                            /*@V75103*/
                                                                     /*@V75103*/
  return ( 0 );                                                      /*@V75103*/
}                                                                    /*@V75103*/

/*------------------------------------*/                            /*@V108555*/
/*                                    */                            /*@VVVVVVV*/
/*                                    */
/*                                    */
/*------------------------------------*/
static SHORT MemCmp( PBYTE s1, PBYTE s2, USHORT n )
{
  SHORT        rc = 0;

  while( n-- )
  {
    if ( rc = *s1++ - *s2++ )
    {
      break;
    }
  }
  return( rc );                                                     /*@VAAAAAA*/
}                                                                   /*@V108555*/

/*------------------------------------*/                            /*@V108555*/
/*                                    */                            /*@VVVVVVV*/
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT CheckSMSEnable()
{
  PBYTE         pBIOS = 0;
  ULONG         ppBIOS;
  USHORT        rc = 0;
  USHORT        ModeFlag;

  /*------------------------------------------------*/
  /* Scan BIOS Region for Promise Controller BIOS   */
  /*                                                */
  /* Promise controllers dont like multiple mode    */
  /* even though the disk reports it supports this  */
  /* feature. This is fixed in later versions of    */
  /* the controller which suppress the reporting    */
  /* of Multiple Mode support in the ATA Identify   */
  /* data.                                          */
  /*------------------------------------------------*/

  for ( ppBIOS = BIOS_MEM_LOW; ppBIOS < BIOS_MEM_HIGH; ppBIOS+=BIOS_SIZE )
  {
    if ( DevHelp_PhysToVirt((ULONG)   ppBIOS,
                            (USHORT)  BIOS_SIZE,
                            (PVOID)   &pBIOS,
                            (PUSHORT) &ModeFlag          ) )
    {
      break;
    }

    if ( !MemCmp( pBIOS+PROMISE_ID_STRING_OFFSET,
                  PromiseIdString,
                  PromiseIdLength )                   )
    {
      rc = 1;
      break;
    }
  }

  return ( rc );                                                    /*@VAAAAAA*/
}                                                                   /*@V108555*/

/****************************/                                       /*@V98451*/
/*                          */                                       /*@VVVVVV*/
/****************************/

USHORT NEAR AllocAdapterResources ( NPATBL npAT )
{
   USHORT         rc = 0;
   RESOURCESTRUCT Resource;
   PAHRESOURCE    pResourceList;

   pResourceList = (PAHRESOURCE)&npAT->ResourceBuf;

   pResourceList->NumResource  = 3;
   pResourceList->hResource[0] = 0L;
   pResourceList->hResource[1] = 0L;
   pResourceList->hResource[2] = 0L;

   /*-----------------------*/
   /* AllocIRQResource      */
   /*-----------------------*/

   Resource.ResourceType              = RS_TYPE_IRQ;
   Resource.IRQResource.IRQLevel      = npAT->IRQLevel;
   Resource.IRQResource.PCIIrqPin     = RS_PCI_INT_NONE;
   Resource.IRQResource.IRQFlags      = RS_IRQ_MULTIPLEXED;
   Resource.IRQResource.Reserved      = 0;
   Resource.IRQResource.pfnIntHandler = 0l;

   if ( (rc = RMAllocResource( hDriver,
                               &pResourceList->hResource[0],
                               &Resource ))  )
   {
     npAT->Status = ATS_ALLOC_IRQ_FAILED;
     goto AllocAdapterResourcesExit;
   }

   /*----------------------*/
   /* AllocIOResource      */
   /*----------------------*/

   Resource.ResourceType              = RS_TYPE_IO;
   Resource.IOResource.BaseIOPort     = npAT->BasePort;
   Resource.IOResource.NumIOPorts     = FI_RFDR;
   Resource.IOResource.IOFlags        = RS_IO_MULTIPLEXED;
   Resource.IOResource.IOAddressLines = 16;

   if ( (rc = RMAllocResource( hDriver,
                               &pResourceList->hResource[1],
                               &Resource ))  )
   {
     npAT->Status = ATS_ALLOC_IO_FAILED;
     RMDeallocResource( hDriver, pResourceList->hResource[0]);
     pResourceList->hResource[0] = 0;
     goto AllocAdapterResourcesExit;
   }

   /*----------------------*/
   /* AllocIOResource      */
   /*----------------------*/

   Resource.ResourceType              = RS_TYPE_IO;
   Resource.IOResource.BaseIOPort     = npAT->BasePort+FI_PDRHD |
                                        ((npAT->BasePort & 0x0200)?
                                                   0x0308 : 0x0300);
   Resource.IOResource.NumIOPorts     = 1;
   Resource.IOResource.IOFlags        = RS_IO_MULTIPLEXED;
   Resource.IOResource.IOAddressLines = 16;

   if ( (rc = RMAllocResource( hDriver,
                               &pResourceList->hResource[2],
                               &Resource ))  )
   {
     npAT->Status = ATS_ALLOC_IO_FAILED;
     RMDeallocResource( hDriver, pResourceList->hResource[0]);
     RMDeallocResource( hDriver, pResourceList->hResource[1]);
     pResourceList->hResource[0] = 0;
     pResourceList->hResource[1] = 0;
     goto AllocAdapterResourcesExit;
   }

   AllocAdapterResourcesExit:

   return( rc );
}

VOID NEAR DeallocAdapterResources ( NPATBL npAT )                 /*@V98451*/
{
   RESOURCESTRUCT Resource;
   PAHRESOURCE    pResourceList;

   pResourceList = (PAHRESOURCE)&npAT->ResourceBuf;

   if(pResourceList->hResource[0])
      RMDeallocResource( hDriver, pResourceList->hResource[0]);  /*V98451*/
   if(pResourceList->hResource[1])
      RMDeallocResource( hDriver, pResourceList->hResource[1]);  /*V98451*/
   if(pResourceList->hResource[2])
      RMDeallocResource( hDriver, pResourceList->hResource[2]);  /*V98451*/


   return;
}


VOID NEAR AssignResources()
{
   UCHAR   i, j, k;
   NPATBL  npAT;
   NPUTBL  npUT;
   NPACB   npACB;
   ADJUNCT Adjunct;
   UCHAR   Buf[42];

   Adjunct.pNextAdj = 0;
   Adjunct.AdjLength = sizeof(ADJUNCT);
   Adjunct.AdjType   = ADJ_ADD_UNIT;
   Adjunct.Add_Unit.ADDHandle = ADDHandle;

   for (i=0, npAT = AdapterTable; i < cAdapters; i++, npAT++ )
   {
      if ( !npAT->cUnits )                                          /*@V108555*/
      {
         continue;
      }

      npACB = npAT->npACB;

      /*---------------------------------------------------*/
      /* increment controller number in adapter string:    */
      /* Original String is "IDE_0 ST506/IDE Controller" */
      /*---------------------------------------------------*/

      AdapterStruct.AdaptDescriptName[4]+=i;

      if (RMCreateAdapter( hDriver,
                           &npAT->hAdapter,
                           &AdapterStruct,
                           (HDEVICE)NULL,
                           (PAHRESOURCE)&npAT->ResourceBuf))
      {
         _asm int 3    /* Need to Halt */
      }

      for (j=0; j < npACB->cUnits; j++ )
      {
         Adjunct.Add_Unit.UnitHandle = (USHORT) &npACB->UnitCB[j];
         DevStruct.pAdjunctList = &Adjunct;

         npUT  = &npAT->Unit[j];

         if (!( npACB->UnitCB[j].Flags & UCBF_ATAPI_DEVICE ) )
         {
            /*-----------------------------------------*/
            /* Copy Device Description to local buffer */
            /*-----------------------------------------*/

            for (k=0; (k < 42) && DevDescriptNameTxt[k] ; k++ )
            {
               Buf[k] = DevDescriptNameTxt[k];
            }

            /* ensure null termination */
            Buf[15] = 0;

            /*---------------------------------------------------*/
            /* increment device number in buffer string:         */
            /* Original String is "HD_0 Hard Drive"              */
            /*---------------------------------------------------*/

            Buf[3]+=j;

            /*--------------------------------------------------*/
            /* if a model number is exists, copy the portion of */
            /* string that will fit in the local buffer         */
            /*--------------------------------------------------*/

            if (npUT->ModelNum[0])
            {
               /* put a space between key and drive info */

               Buf[4] = ' ';

               for (k=5; (k < 42) && npUT->ModelNum[k-5]; k++)
               {
                  Buf[k] = npUT->ModelNum[k-5];
               }

               /* ensure we are null terminated */

               Buf[41] = 0;
            }

            DevStruct.DevDescriptName = (PSZ) &Buf;

            if ( RMCreateDevice( hDriver,
                                 &npACB->UnitCB[j].hDevice,
                                 &DevStruct,
                                 npAT->hAdapter,
                                 NULL))
            {
               _asm int 3
            }
         }
      }
   }
}                                                                    /*@VAAAAA*/
                                                                     /*@V98451*/
