/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/* SCCSID = src/dev/dasd/cdrom/ibm1s506/s506geom.c, idskflt, c.basedd 99/03/25 */
/****************************************************************************
 *                                                                          *
 *                           OCO Source Materials                           *
 *                                                                          *
 *                                                                          *
 ****************************************************************************/
/**@internal s506geom.c
 *  Determine the drive's physical and logical geometry.  Initialization time
 *  only.
 * @version 1.00
 * @context 16-bit, Ring-0, device driver initialization.
 * @history
 *  02/20/98 192176 Vince Rapp      New file.  Some routines were moved to this
 *   file from s506init.c in order to reduce the size of s506init.c
 *  04/24/98 195918 Vince Rapp      Increased VerifyEndOfMedia() back-off
 *   algorithm to be 12.5% of total disk size vs the existing arbitrary 10
 *   cylinders.
 *  10/16/98 198114 Vince Rapp      Provide a default value for the UCB's sectors
 *                                  per block (interrupt.)
 *  03/24/99 221835 Vince Rapp      Some BIOSs were hanging when we failed to do
 *                                  ATA identify and continued anyway on through
 *                                  DetermineUnitGeometry().
 *  02/22/00 237424 R Lewandowski   Enable LBAMode on Large drives.
 *
 *  12/12/01  d_267584 dimir - added support of new FAT16 partition
 *                           of type 0x0E (the same as type 0x06) and
 *                           new extended partition of type 0x0F
 *                           (the same as type 0x05). Partitions of
 *                           these types are located beyond 7.875GB limit.
 *
 */

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#define INCL_DOSINFOSEG
#define INCL_NO_SCB
#define INCL_INITRP_ONLY
#define INCL_DOSERRORS
#include <os2.h>
#include <dskinit.h>

#include <iorb.h>
#include <reqpkt.h>
#include <dhcalls.h>
#include <addcalls.h>

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


//
//   DetermineUnitGeometry()
//
// Extract and determine the logical and physical geometry.
//
USHORT DetermineUnitGeometry( NPATBL npAT, USHORT UnitId )
{
   NPACB      npACB;
   NPUTBL     npUT;
   NPUCB      npUCB;
   NPGEOMETRY npGEO;
   ULONG      MaxSec;
   ULONG      SecPerCyl;
   USHORT     rc;

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

   // Get the hardware reported Physical geometry.                  /*@V192176*/
   if( !IdentifyGeometryGet( npAT, UnitId ) )                       /*@V192176*/
   {                                                                /*@V192176*/
      npUT->Flags |= UTBF_IDEGEOMETRYVALID;                         /*@V192176*/
                                                                    /*@V192176*/
      // Unit successfully idenified so reset its Status.            /*@157085*/
      npUT->Status = UTS_OK;                                         /*@157085*/
   }                                                                /*@V192176*/
   // Start defect: 221835
   else
   {
      // Must be able to get the IDE (physical) geometry or else
      // can't use the drive as an ATA disk and can't continue with
      // geometry determination because some BIOSs will hang.
      npUT->Status = UTS_READ_0_FAILED;

      return( npUT->Status );
   }
   // End defect: 221835
                                                                    /*@V192176*/
   //                                                               /*@V192176*/
   // Reconsile the physical geometries.                            /*@V192176*/
   //                                                               /*@V192176*/
   if( npUT->Flags & UTBF_CMDGEOMETRYVALID )                        /*@V192176*/
   {                                                                /*@V192176*/
      // User/command line override was specified.                  /*@V192176*/
      npUCB->PhysGeom  = npUT->CMDPhysGeom;                         /*@V192176*/
      npUCB->WrtPreCmp = npUT->CMDWrtPreCmp;                        /*@V192176*/
   }                                                                /*@V192176*/
   else if( npUT->Flags & UTBF_IDEGEOMETRYVALID )                   /*@V192176*/
   {                                                                /*@V192176*/
      // Hardware reported geometry is good.                        /*@V192176*/
      npUCB->PhysGeom  = npUT->IDEPhysGeom;                         /*@V192176*/
      npUCB->WrtPreCmp = -1;                                        /*@V192176*/
                                                                    /*@V192176*/
      if( npUCB->PhysGeom.TotalSectors >                            /*@V192176*/
          TRANSLATED_MAX_TOTALSECTORS &&                            /*@V192176*/
          npUCB->PhysGeom.NumHeads == LIMITED_MAX_NUMHEADS )        /*@V192176*/
      {                                                             /*@V192176*/
         // This is a large drive (>7.9GB) and is reporting         /*@V192176*/
         // 16 heads.  Per the spec, use 15 heads for the physical  /*@V192176*/
         // geometry to ensure compatibility with the first 7.9GB.  /*@V192176*/
         npUCB->PhysGeom.NumHeads = LIMITED_MAX_NUMHEADS - 1;       /*@V192176*/
      }                                                             /*@V192176*/
   }                                                                /*@V192176*/
   else                                                             /*@V192176*/
   {                                                                /*@V192176*/
      npUT->Status = UTS_NOT_PRESENT;                               /*@V192176*/
   }                                                                /*@V192176*/
                                                                    /*@V192176*/
   // Calculate a logical geometry based on the phyiscal geometry.  /*@V192176*/
   if( LogGeomCalculate( &npUT->IDELogGeom, npUCB->PhysGeom ) )     /*@V192176*/
   {                                                                /*@V192176*/
      npUT->Flags &= ~UTBF_IDEGEOMETRYVALID;                        /*@V192176*/
   }                                                                /*@V192176*/
                                                                    /*@V192176*/
   // Get the BIOS Parameter Block Logical geometry for this drive. /*@V192176*/
   if( !BPBGeometryGet( npAT, UnitId ) )                            /*@V192176*/
   {                                                                /*@V192176*/
      npUT->Flags |= UTBF_BPBGEOMETRYVALID;                         /*@V192176*/
   }                                                                /*@V192176*/
                                                                    /*@V192176*/
   // Get the BIOS reported and Logical geometry.                   /*@V192176*/
   if( !Int13GeometryGet( npAT, UnitId ) )                          /*@V192176*/
   {                                                                /*@V192176*/
      npUT->Flags |= UTBF_I13GEOMETRYVALID;                         /*@V192176*/
   }                                                                /*@V192176*/
                                                                    /*@V192176*/
   //                                                               /*@V192176*/
   // Reconsile the logical geometries.                             /*@V192176*/
   //                                                               /*@V192176*/
   if( npUT->Flags & UTBF_CMDGEOMETRYVALID )                        /*@V192176*/
   {                                                                /*@V192176*/
      // User/command line override was specified.                  /*@V192176*/
      npUCB->LogGeom   = npUT->CMDLogGeom;                          /*@V192176*/
   }                                                                /*@V192176*/
   else if( npUT->Flags & UTBF_BPBGEOMETRYVALID )                   /*@V192176*/
   {                                                                /*@V192176*/
      // BIOS Parameter Block from disk is good.                    /*@V192176*/
      npUCB->LogGeom   = npUT->BPBLogGeom;                          /*@V192176*/
   }                                                                /*@V192176*/
   else if( npUT->Flags & UTBF_I13GEOMETRYVALID )                   /*@V192176*/
   {                                                                /*@V192176*/
      // BIOS reported geometry is good.                            /*@V192176*/
      npUCB->LogGeom   = npUT->I13LogGeom;                          /*@V192176*/
   }                                                                /*@V192176*/
   else if( npUT->Flags & UTBF_IDEGEOMETRYVALID )                   /*@V192176*/
   {                                                                /*@V192176*/
      // Hardware reported geometry is good.                        /*@V192176*/
      npUCB->LogGeom   = npUT->IDELogGeom;                          /*@V192176*/
   }                                                                /*@V192176*/
   else                                                             /*@V192176*/
   {
      npUT->Status = UTS_NOT_PRESENT;
   }

   //
   // The physical geometry is the best source of the total number  /*@V192176*/
   // of user addressable sectors.                                  /*@V192176*/
   //                                                               /*@V192176*/
   npUCB->LogGeom.TotalSectors = npUCB->PhysGeom.TotalSectors;      /*@V192176*/

   // Find the last fully accessable cylinder.  Recompute the
   // number of logical and physical cylinders only if this was
   // a command line entered geometry.
   if( !VerifyEndOfMedia( npAT, UnitId, &npUCB->LogGeom, (PULONG)&MaxSec ) )
   {
      if( !(npUT->Flags & UTBF_CMDGEOMETRYVALID) ||                 /*@V194527*/
          !npUT->CMDPhysGeom.SectorsPerTrack )                      /*@V194527*/
      {                                                             /*@V194527*/
         /*---------------------------------------------*/
         /* 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;
         npGEO->TotalSectors = npGEO->TotalCylinders *              /*@V192176*/
                               (ULONG)npGEO->NumHeads *             /*@V192176*/
                               (ULONG)npGEO->SectorsPerTrack;       /*@V192176*/
      }                                                             /*@V194527*/

      if( !(npUT->Flags & UTBF_CMDGEOMETRYVALID) ||                 /*@V194527*/
          !npUT->CMDLogGeom.SectorsPerTrack )                       /*@V194527*/
      {                                                             /*@V194527*/
         /*---------------------------------------------*/
         /* Adjust the cylinder count in the logical    */
         /* geometry to the full capacity of the disk.  */
         /*---------------------------------------------*/
         npGEO = &npUCB->LogGeom;

         npGEO->TotalSectors   = MaxSec;
         SecPerCyl = npGEO->SectorsPerTrack * npGEO->NumHeads;
         npGEO->TotalCylinders = MaxSec / SecPerCyl;
         npGEO->BytesPerSector = 512;
         npGEO->TotalSectors = npGEO->TotalCylinders *              /*@V192176*/
                               (ULONG)npGEO->NumHeads *             /*@V192176*/
                               (ULONG)npGEO->SectorsPerTrack;       /*@V192176*/
      }                                                             /*@V194527*/
   }
   else
   {
      npUT->Status = UTS_READ_0_FAILED;
   }

   return( npUT->Status );
}


//
//   IdentifyGeometryGet()
//
// Perform an ATA identify function to get the drive's IDE geometry.  This is
// the best source of the physical geometry.  Calculate a corresponding logical
// geometry.  The calculated logical geometry will only be used as a last
// resort.
// This routine also checks for and enables drive capabilities based on the
// content of the drive's identify data.
//
BOOL NEAR IdentifyGeometryGet( NPATBL npAT,
                               USHORT UnitId )
{
   NPUTBL          npUT;
   NPACB           npACB;
   NPUCB           npUCB;
   NPIDENTIFYDATA  npID;
   USHORT          WDIDValid;

   npUT  = &npAT->Unit[UnitId];
   npACB = npAT->npACB;
   npUCB = &npACB->UnitCB[UnitId];
   npID = (NPIDENTIFYDATA)ScratchBuf;

   // Send the identify function to get the drive's identify data.
   npACB->TimeOut    = INIT_TIMEOUT_SHORT;
   npACB->IRQTimeOut = INIT_TIMEOUT_SHORT;

   if( !DoIdentify( npAT, UnitId, npID ) )
   {
      // The routines in the following block have nothing to do with
      // the drive's geometry, but do require the drive's identify
      // data, so they are called here for convienence of accessing
      // the identify data.
      {
         WDIDValid = IDStringsExtract( npUT, npID );

         MediaStatusEnable( npACB, npUCB, UnitId, npID );

         MultipleModeEnable( npACB, npUCB, npUT, npID );

         LBAModeEnable( npACB, npUCB, npUT, npID );

         /*---------------------------------------*/
         /* Check for DMA and PIO Support         */
         /*                                       */
         /*---------------------------------------*/
         /* Begin [001.3] [002.2] Check for DMA support and enable if         */
         /*    present.  Add support for Bus Master timing information        */
         /* [004.2] Modify handling of Bus Master DMA disable switches        */
         /* If we want to support DMA timing and the drive supports DMA       */
         UCBSetupDMAPIO( npID, npAT, npUCB, WDIDValid );            /*@V179942*/
      }

      // Get the physical geometry from the identify data.
      IDEGeomExtract( &npUT->IDEPhysGeom, npID, npUCB );            

      // Verify the extracted physcial geometry is reasonable.
      if( !PhysGeomValidate( npUT->IDEPhysGeom )  )
      {
         return( FALSE );
      }
   }

   // Something failed so default to most conservative, least
   // capability, options.
   return( TRUE );
}


//
//   IDEGeomExtract()
//
// Extract the physcial (default) geometry from the drive's identify
// data and determine the number of user accessable sectors the drive
// contains.
// If this is a large drive then the reported total number of sectors
// will not match the geometry and this routine will report the IDE
// physical geometry as invalid.  Such large drives are expected to
// contain more than: 15,481,935 user accessable sectors.
// (63 SectorsPerTrack * 15 Heads * 16383 Cylinders)
//
VOID NEAR IDEGeomExtract( NPGEOMETRY     npIDEGeom,
                          NPIDENTIFYDATA npId ,
                          NPUCB          npUCB )                    
{
   ULONG   legacyTotalSectors;

   // The ATA Identify data contains the physical, aka default,
   // IDE geometry in words 1, 3, and 6:
   //    word 1 = total cylinders
   //    word 3 = total heads
   //    word 6 = default sectors per track
   // Note: if total cylinders is reported as 0, it should be
   // interpreted as the maximum number of ATA cylinders.
   npIDEGeom->SectorsPerTrack = npId->SectorsPerTrack;
   npIDEGeom->TotalCylinders  = ( npId->TotalCylinders ) ?
      npId->TotalCylinders : ATA_MAX_CYLINDERS;
   npIDEGeom->NumHeads        = npId->NumHeads;

   // Compute the drive's legacy capacity based on the ATA Identify
   // reported physical (default) geometry.
   legacyTotalSectors = npIDEGeom->SectorsPerTrack *
                        npIDEGeom->TotalCylinders *
                        npIDEGeom->NumHeads;

   // The identify data also contains the drive's actual capacity.
   // If drive's actual capacity is larger than the capacity
   // implied from the above physical geometry, then to access the
   // entire drive via BIOS, Enhanced BIOS support is required.
   if( npId->LBATotalSectors == 0 )
   {
      // This is an old drive and has no LBA support and the reported
      // physical geometry contains the entire drive.
      npIDEGeom->TotalSectors = legacyTotalSectors;
   }
   else if( legacyTotalSectors == npId->LBATotalSectors )
   {
      // This drive has LBA support and is small enough that the
      // reported physical geometry contains the entire drive.

      npIDEGeom->TotalSectors = legacyTotalSectors;
   }
   else
   {
      // This is a new large drive, the entire drive cannot be
      // accessed from reported physical geometry.  The IDE reported
      // total number sectors does contain the entire drive.

      npIDEGeom->TotalSectors = npId->LBATotalSectors;

      npUCB->Flags |= UCBF_LBAMODE; /* Turn LBA Mode on for drive */
   }

   return;
}


//
//   LogGeomCalculate()
//
// Calculate a "best guess" logical geometry for this drive based
// entirely on the input physcial geometry.
//
BOOL NEAR LogGeomCalculate( NPGEOMETRY npLogGeom,
                            GEOMETRY   PhysGeom )
{
   ULONG   legacyTotalSectors;

   // Compute the drive's legacy capacity based on the ATA Identify
   // reported physical (default) geometry.
   legacyTotalSectors = PhysGeom.SectorsPerTrack *
                        PhysGeom.TotalCylinders *
                        PhysGeom.NumHeads;

   if( PhysGeom.TotalCylinders  <= BIOS_MAX_CYLINDERS &&
       PhysGeom.NumHeads        <= BIOS_MAX_NUMHEADS &&
       PhysGeom.SectorsPerTrack <= BIOS_MAX_SECTORSPERTRACK )
   {
      // This is a small drive, < 528MB.  No translation or enhanced
      // BIOS is required to access the entire drive.  Use the physical
      // geometry as the logical geometry.
      *npLogGeom = PhysGeom;
   }
   else if( PhysGeom.TotalSectors > TRANSLATED_MAX_TOTALSECTORS )
   {
      // The drive is large (> 7.9 GigaBytes) and is reporting its physical
      // geometry as larger that the largest compatible legacy geometry.
      // If this legacy geometry is used as the logical geometry then we
      // will not see the entire drive.  Asjust the logical geometry.

      // Leave number of heads the same as the HW has reported it to be.
      npLogGeom->NumHeads = PhysGeom.NumHeads;

      // Leave sectors per track the same as the HW has reported it to be.
      npLogGeom->SectorsPerTrack = PhysGeom.SectorsPerTrack;

      // Expand the logical geometry to the entire drive by increasing the
      // number of logical cylinders.
      npLogGeom->TotalCylinders  = PhysGeom.TotalSectors/
         (npLogGeom->NumHeads * npLogGeom->SectorsPerTrack);

   }
   else if( PhysGeom.TotalSectors > 1 &&
            PhysGeom.SectorsPerTrack == BIOS_MAX_SECTORSPERTRACK )
   {
      // This drive requires a CHS translation but no enhanced BIOS to
      // access the entire drive.
      // Calculate a logical geometry using the LBA Assist Translation
      // algorithm.
      if( LogGeomCalculateLBAAssist( npLogGeom, PhysGeom ) )
      {
         return( TRUE );
      }
   }
   else if( PhysGeom.NumHeads > 1 &&
            PhysGeom.TotalCylinders > BIOS_MAX_CYLINDERS )
   {
      // This drive requires a CHS translation but no enhanced BIOS to
      // access the entire drive.
      // Calculate a logical geometry using the Bit Shift Translation
      // algorithm.
      if( LogGeomCalculateBitShift( npLogGeom, PhysGeom ) )
      {
         return( TRUE );
      }
   }
   else
   {
      // Don't know how to translate this geometry.
      return( TRUE );
   }

   return( FALSE );
}


//
//   LogGeomCalculateLBAAssist()
//
// Calculate the logical geometry based on the input physcial geometry
// using the LBA Assist Translation algorithm.
//
BOOL NEAR LogGeomCalculateLBAAssist( NPGEOMETRY npLogGeom,
                                     GEOMETRY   PhysGeom )
{
   USHORT numHeads;

   npLogGeom->SectorsPerTrack = LIMITED_MAX_SECTORSPERTRACK;

   if( PhysGeom.TotalSectors <=
       (BIOS_MAX_CYLINDERS * 16 * BIOS_MAX_SECTORSPERTRACK) )
   {
      numHeads = 16;
   }
   else if( PhysGeom.TotalSectors <=
            (BIOS_MAX_CYLINDERS * 32 * BIOS_MAX_SECTORSPERTRACK) )
   {
      numHeads = 32;
   }
   else if( PhysGeom.TotalSectors <=
            (BIOS_MAX_CYLINDERS * 64 * BIOS_MAX_SECTORSPERTRACK) )
   {
      numHeads = 64;
   }
   else if( PhysGeom.TotalSectors <=
            (BIOS_MAX_CYLINDERS * 128 * BIOS_MAX_SECTORSPERTRACK) )
   {
      numHeads = 128;
   }
   else if( PhysGeom.TotalSectors <=
            (BIOS_MAX_CYLINDERS * 255 * BIOS_MAX_SECTORSPERTRACK) )
   {
      numHeads = 255;
   }
   else
   {
      return( TRUE );
   }

   npLogGeom->TotalCylinders = PhysGeom.TotalSectors /
      (numHeads * BIOS_MAX_SECTORSPERTRACK);
   npLogGeom->NumHeads       = numHeads;
   npLogGeom->TotalSectors   = PhysGeom.TotalSectors;

   return( FALSE );
}


//
//   LogGeomCalculateBitShift()
//
// Calculate the logical geometry based on the input physical geometry
// using the Bit Shift Translation algorithm.
//
BOOL NEAR LogGeomCalculateBitShift( NPGEOMETRY npLogGeom,
                                    GEOMETRY   PhysGeom )
{
   USHORT shiftBits = 0;

   npLogGeom->SectorsPerTrack = PhysGeom.SectorsPerTrack;

   if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 2 &&
       PhysGeom.NumHeads <= LIMITED_MAX_NUMHEADS )
   {
      shiftBits;
   }
   else if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 4 &&
            PhysGeom.NumHeads <= LIMITED_MAX_NUMHEADS )
   {
      shiftBits = 2;
   }
   else if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 8 &&
            PhysGeom.NumHeads <= LIMITED_MAX_NUMHEADS )
   {
      shiftBits = 3;
   }
   else if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 16 &&
            PhysGeom.NumHeads <= LIMITED_MAX_NUMHEADS )
   {
      shiftBits = 4;
   }
   else if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 32 &&
            PhysGeom.NumHeads <= (LIMITED_MAX_NUMHEADS / 2) )
   {
      shiftBits = 5;
   }
   else if( PhysGeom.TotalCylinders <= LIMITED_MAX_CYLINDERS * 64 &&
            PhysGeom.NumHeads <= (LIMITED_MAX_NUMHEADS / 4) )
   {
      shiftBits = 6;
   }
   else
   {
      return( TRUE );
   }

   npLogGeom->TotalCylinders = PhysGeom.TotalCylinders >> shiftBits;
   npLogGeom->NumHeads       = PhysGeom.NumHeads << shiftBits;
   npLogGeom->TotalSectors   = PhysGeom.TotalSectors;

   return( FALSE );
}


//
//   Int13GeometryGet()
//
BOOL NEAR Int13GeometryGet( NPATBL npAT,
                            USHORT UnitId )
{
   PROMCFG       pROMCFG;
   NPUTBL        npUT;
   NPGEOMETRY    npGEO;
   USHORT        DriveId;
   BOOL          rc;
   USHORT        i = 0;

   npUT  = &npAT->Unit[UnitId];

   // Determine if the BIOS supports sufficient enhanced functions.
   rc = Int13CheckExtensionsPresent( npUT );

   // Get the BIOS logical geometry from legacy Int 13h, function 8h.
   rc = Int13Fun08GetLogicalGeom( npUT );

   return( rc );
}


//                                                                  /*@V179486*/
//   BPBGeometryGet()                                               /*@VVVVVVV*/
//
// Read the media's BIOS Parameter Block values for sectors per track and
// number of heads.  Return FALSE if valid information is being returned and
// TRUE if a valid BIOS Parameter Block could not be located.
//
BOOL NEAR BPBGeometryGet( NPATBL npAT,
                          USHORT UnitId )
{
   NPUTBL                npUT  = &npAT->Unit[UnitId];
   NPACB                 npACB = npAT->npACB;
   USHORT                i, j;
   UCHAR                 partitionType;
   ULONG                 extendedPartitionStartSector;
   ULONG                 partitionBootRecordSector;
   BOOL                  containsExtendedPartition;

   // Initialize the return values.

   npUT->BPBLogGeom.TotalCylinders  = 0;
   npUT->BPBLogGeom.NumHeads        = 0;
   npUT->BPBLogGeom.SectorsPerTrack = 0;

   // Locate the partition table.  Read Sector (LBA) 0, the Master
   // Boot Record (MBR) and verify its signature.

   npACB->UnitCB[UnitId].ReqFlags = (UCBR_RECAL | UCBR_SETPARAM);

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

   if( ReadDrive( npACB, UnitId, 0, 1, (PBYTE)&BootRecord ) ||
       BootRecord.signature != BR_SIGNATURE )
   {
      return TRUE;
   }

   // Look for Ez-Drive and On-Track drives.  If detected, reread   /*@V192176*/
   // the partition table from the offset MBR and for               /*@V192176*/
   //    On-Track: enable a 63 block offset and CHS translation     /*@V192176*/
   //    Ez-Drive: enable floppy boot                               /*@V192176*/
   // then use the Ez-Drive partition table in LBA 1.               /*@V192176*/
   for( i = 0; i < BR_MAX_PARTITIONS; i++ )                         /*@V192176*/
   {                                                                /*@V192176*/
      partitionType = BootRecord.partitionTable[i].systemIndicator; /*@V192176*/
                                                                    /*@V192176*/
      if( partitionType == BR_PARTTYPE_ONTRACK )                    /*@V192176*/
      {                                                             /*@V192176*/
         // For On-Track controlled drives, the partiton table      /*@V192176*/
         // should be in LBA 63.  Reread the MBR from LBA 1.        /*@V192176*/
         if( ReadDrive( npACB, UnitId, ONTRACK_SECTOR_OFFSET,       /*@V192176*/
                        1, (PBYTE)&BootRecord ) ||                  /*@V192176*/
             BootRecord.signature != BR_SIGNATURE )                 /*@V192176*/
         {                                                          /*@V192176*/
            return TRUE;                                            /*@V192176*/
         }                                                          /*@V192176*/
         else                                                       /*@V192176*/
         {                                                          /*@V192176*/
            // This is an On-Track controlled drive.                /*@V192176*/
            npAT->npACB->UnitCB[UnitId].Flags |= UCBF_ONTRACK;      /*@V192176*/
            break;                                                  /*@V192176*/
         }                                                          /*@V192176*/
      }                                                             /*@V192176*/
      else if( partitionType == BR_PARTTYPE_EZDRIVE )               /*@V192176*/
      {                                                             /*@V192176*/
         // For Ez-Drive controlled drives, the partiton table      /*@V192176*/
         // should be in LBA 1.  Reread the MBR from LBA 1.         /*@V192176*/
         if( ReadDrive( npACB, UnitId, 1,                           /*@V192176*/
                        1, (PBYTE)&BootRecord ) ||                  /*@V192176*/
             BootRecord.signature != BR_SIGNATURE )                 /*@V192176*/
         {                                                          /*@V192176*/
            return TRUE;                                            /*@V192176*/
         }                                                          /*@V192176*/
         else                                                       /*@V192176*/
         {                                                          /*@V192176*/
            // This is an Ez-Drive controlled drive.                /*@V192176*/
            npAT->npACB->UnitCB[UnitId].Flags |= UCBF_EZDRIVEFBP;   /*@V192176*/
            break;                                                  /*@V192176*/
         }                                                          /*@V192176*/
      }                                                             /*@V192176*/
   }                                                                /*@V192176*/

   // Look for a Primary Partition from which the BPB fields can be extracted.

   for( i = 0; i < BR_MAX_PARTITIONS; i++ )
   {
      partitionType = BootRecord.partitionTable[i].systemIndicator;
      if( partitionType == BR_PARTTYPE_FAT01 ||
          partitionType == BR_PARTTYPE_FAT12 ||
          partitionType == BR_PARTTYPE_FAT16 ||
          partitionType == BR_PARTTYPE_FAT16X||  //d_267584
          partitionType == BR_PARTTYPE_OS2IFS )
      {
         // Read this primary partition's first block.  The first block is a
         // Partition Boot Record which contains the BIOS Parameter Block.

         partitionBootRecordSector = BootRecord.partitionTable[i].offset;
         if( !ReadAndExtractBPB( npACB, UnitId, partitionBootRecordSector,
                                 &npUT->BPBLogGeom ) )
         {
            // Found a valid BPB, all done!
            return FALSE;
         }
         else
         {
            continue;
         }
      }
   }

   //
   // No primary partition, so look for an extended partition and follow the
   // chain of extended partitions if necessary to get to a valid partition
   // boot record.

   for( i = 0; i < BR_MAX_PARTITIONS; i++ )
   {
      partitionType = BootRecord.partitionTable[i].systemIndicator;
//d_267584      if( partitionType == BR_PARTTYPE_EXTENDED )
      if (partitionType == BR_PARTTYPE_EXTENDED || partitionType == BR_PARTTYPE_EXTENDEDX) //d_267584
      {
         extendedPartitionStartSector = BootRecord.partitionTable[i].offset;
         containsExtendedPartition = TRUE;
         while( containsExtendedPartition )
         {
            // This is an extended partition.  The first sector of an extended
            // partition has the same format as a Master Boot Record but is
            // called an Extended Boot Record.  There is a limit of 1 extended
            // partition per extended partition but this relationship is
            // recursive.

            if( ReadDrive( npACB, UnitId, extendedPartitionStartSector, 1,
                           (PBYTE)&ExtendedBootRecord ) ||
                ExtendedBootRecord.signature != BR_SIGNATURE )
            {
               containsExtendedPartition = FALSE;
            }
            else
            {
               // Look through the Extended Boot Record for a valid logical
               // block device partition.

               for( j = 0; j < BR_MAX_PARTITIONS; j++ )
               {
                  partitionType = ExtendedBootRecord.partitionTable[j].
                                  systemIndicator;
                  if( partitionType == BR_PARTTYPE_FAT01 ||
                      partitionType == BR_PARTTYPE_FAT12 ||
                      partitionType == BR_PARTTYPE_FAT16 ||
                      partitionType == BR_PARTTYPE_FAT16X||  //d_267584
                      partitionType == BR_PARTTYPE_OS2IFS )
                  {
                     partitionBootRecordSector = extendedPartitionStartSector +
                        ExtendedBootRecord.partitionTable[j].offset;
                     if( !ReadAndExtractBPB( npACB, UnitId,
                                             partitionBootRecordSector,
                                             &npUT->BPBLogGeom ) )
                     {
                        // Found a valid BPB, all done!
                        return FALSE;
                     }
                  }
               }

               // Did not find an extended logical device partition, so look
               // through this Extended Boot Record once more to find another
               // extended partition contained within the current extended
               // partition.

               containsExtendedPartition = FALSE;  // Assume there are no more
               for( j = 0; j < BR_MAX_PARTITIONS; j++ )
               {
                  partitionType =
                     ExtendedBootRecord.partitionTable[j].systemIndicator;
//d_267584                  if( partitionType == BR_PARTTYPE_EXTENDED )
                  if (partitionType == BR_PARTTYPE_EXTENDED || partitionType == BR_PARTTYPE_EXTENDEDX) //d_267584
                  {
                     containsExtendedPartition = TRUE;
                     extendedPartitionStartSector +=
                        ExtendedBootRecord.partitionTable[j].offset;
                     // Only 1 extended partition can be contained within an
                     // extended partition, so OK to quit.
                     break;
                  }
               }
            }
         }
      }
   }

   return TRUE;  // Valid BPB was not found                         /*@AAAAAAA*/
}                                                                   /*@V179486*/


//
//   ReadAndExtractBPB()
//
// Read the indicated sector, and interpret it as a Partition Boot Record.
// Verify the sector is a valid Partition Boot Record and if so return the
// BIOS Parameter Block's number of heads and sectors per track.  Return FALSE
// if valid data is being returned in these parameters and return TRUE if
// the BPB data is not valid.
//
BOOL NEAR ReadAndExtractBPB( NPACB      npACB,
                             USHORT     UnitId,
                             ULONG      partitionBootRecordSectorNum,
                             NPGEOMETRY npLogGeom )
{
   if( ReadDrive( npACB,
                  UnitId,
                  partitionBootRecordSectorNum,
                  1,
                  (PBYTE)&PartitionBootRecord ) )
   {
      return TRUE;
   } else {

      // Validate the Partition Boot Record.
      // Check signature?? partitionBootRecord.signature
      // Extract the BPB fields.

      if( (PartitionBootRecord.sectorsPerTrack > 0)                       &&
          (PartitionBootRecord.heads > 0)                                 &&
          (PartitionBootRecord.sectorsPerTrack <= BR_MAX_SECTORSPERTRACK) &&
          (PartitionBootRecord.heads <= BR_MAX_HEADS)                        )

      {
         npLogGeom->SectorsPerTrack = PartitionBootRecord.sectorsPerTrack;
         npLogGeom->NumHeads        = PartitionBootRecord.heads;
         return FALSE;

      } else {

         return TRUE;
      }
   }
}


//
//   VerifyEndofMedia()
//
// Read sectors starting with the last LBA on the drive.  If a sector
// is not readable, then back off by 1 cylinder and try again.  Return
// the last LBA of the largest cylinder that is completely readable.
//
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;

  npGEO = &npUCB->PhysGeom;
  c = npGEO->NumHeads * npGEO->SectorsPerTrack;

  if( c )                                                           /*@V192176*/
  {                                                                 /*@V192176*/
     // The input geometry must also specify the total number of    /*@V192176*/
     // user addressable sectors.                                   /*@V192176*/
     MaxLBA = (npGEOC->TotalSectors / c) * c;                       /*@V192176*/

     // Back-off by as much as 12.5% of the drive's reported total  /*@V196345*/
     // number of sectors.                                          /*@V196345*/
     MinLBA = MaxLBA - (npGEOC->TotalSectors >> 3);                 /*@V196345*/

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

  if( rc )                                                          /*@V192176*/
  {                                                                 /*@V192176*/
     *MaxSec = 0;                                                   /*@V192176*/
  }                                                                 /*@V192176*/
  else                                                              /*@V192176*/
  {                                                                 /*@V192176*/
     // If this is an On-Track drive, subtract off the first track  /*@V192176*/
     // as this data is not useable by the OS.                      /*@V192176*/
     if( npAT->npACB->UnitCB[UnitId].Flags & UCBF_ONTRACK )         /*@V192176*/
     {                                                              /*@V192176*/
        *MaxSec = r - ONTRACK_SECTOR_OFFSET;                        /*@V192176*/
     }                                                              /*@V192176*/
     else                                                           /*@V192176*/
     {                                                              /*@V192176*/
        *MaxSec = r;                                                /*@V192176*/
     }                                                              /*@V192176*/
  }                                                                 /*@V192176*/

  return ( rc );
}


//
//   PhysGeomValidate()
//
BOOL NEAR PhysGeomValidate( GEOMETRY Geom )
{
   if( (Geom.TotalCylinders > 1) &&
       (Geom.NumHeads > 1) && (Geom.NumHeads <= ATA_MAX_NUMHEADS) &&
       (Geom.SectorsPerTrack > 1) && (Geom.SectorsPerTrack <= ATA_MAX_SECTORSPERTRACK) )
   {
      return( FALSE );
   }

   return( TRUE );
}


//
//   LogGeomValidate()
//
BOOL NEAR LogGeomValidate( GEOMETRY Geom )
{
   if( (Geom.TotalCylinders > 1) &&
       (Geom.NumHeads > 1) && (Geom.NumHeads <= BIOS_MAX_NUMHEADS) &&
       (Geom.SectorsPerTrack > 1) && (Geom.SectorsPerTrack <= BIOS_MAX_SECTORSPERTRACK) )
   {
      return( FALSE );
   }

   return( TRUE );
}


//
//   IDStringsExtract()
//
// Extract the identification strings from the identify data.  Look for
// the Western Digital signature.
//
BOOL NEAR IDStringsExtract( NPUTBL         npUT,
                            NPIDENTIFYDATA npID )
{
   BOOL   WDIDValid = TRUE;   /* [001.3] indicate WDC drive (default) */
   USHORT i;                  /* [001.3] */

   /*---------------------------------------*/                      /*@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*/

   /***************************************/
   /* Check for Western Digital signature */
   /***************************************/
   for (i=0; i < 4; i++)           /* check first four bytes for "WDC " */
   {
      if ( npID->ModelNum[i] != WDCModel[i] )
      {
      WDIDValid = FALSE;      /* no match, clear signature ok flag */
      break;                                                        /*@V151345*/
      }
   }

   if ( WDIDValid )
   {
      SystemFlags |= SYSFLG_HASWDC; /* at least one WDC drive in system */
   }

   return( WDIDValid );
}


//
//   MediaStatusEnable()
//
// Determine if this drive supports Media Status reporting.
//
BOOL NEAR MediaStatusEnable( NPACB          npACB,
                             NPUCB          npUCB,
                             USHORT         UnitId,
                             NPIDENTIFYDATA npID )
{
   BOOL   rc = TRUE;       // default to not enabled

   // check for media status support
   if( npID->MediaStatusWord & 1 )
   {
      // Media Status reporting is supported
      npUCB->Flags |= UCBF_MEDIASTATUS;
      if( !IssueSetFeatures( npACB, UnitId, FX_ENABLE_MEDIA_STATUS, 0 ) )
      {
         npUCB->Flags |= UCBF_REMOVABLE;           // indicate removable media
         npUCB->DriveCapability = npACB->icp.TaskFileOut.CylinderHigh;/*@V155162*/
         if( IssueGetMediaStatus( npACB, UnitId ) != IOERR_CMD_ABORTED )
         {
            npUCB->MediaStatus = npACB->icp.TaskFileOut.Error;        /*@V155162*/
            IssueSetFeatures( npACB, UnitId, FX_DISABLE_MEDIA_STATUS, 0 );
         }
      }
      rc = FALSE;
   }

   return( rc );
}


//
//   MultipleModeEnable()
//
// Determine if this drive supports transfering multiple blocks
// blocks per interrupt.
//
BOOL NEAR MultipleModeEnable( NPACB          npACB,
                              NPUCB          npUCB,
                              NPUTBL         npUT,
                              NPIDENTIFYDATA npID )
{
   USHORT cSec;
   USHORT cCap;                                                     /*@V127556*/
   BOOL   rc = TRUE;       // default to not enabled

   /*---------------------------------------------------------*/
   /* 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*/
   /* Note: Or Bit 1 of AdditionalWordsValid which is a       */    /*@V127556*/
   /*       validation bit for the DMA information stored     */    /*@V127556*/
   /*       later in the Identify block.  If this bit is set  */    /*@V127556*/
   /*       then the drive is assumed to be relatively new    */    /*@V127556*/
   /*       and as such supports SMS.                         */    /*@V127556*/
   /*                                                         */
   /*---------------------------------------------------------*/

   npUCB->SecPerBlk = 1;   // default value                         //@V198114

   cSec = npID->NumSectorsPerInt;
   cCap = npID->AdditionalWordsValid;                               /*@V127556*/

   if( npUT->Flags & UTBF_SMSENABLED ||
       ( ((UCHAR)cSec) &&
         ((cSec & FX_SECPERINTVALID) ||                             /*@V127556*/
          (cCap & FX_WORDS64_70VALID)) ) )                          /*@V127556*/
   {
      npUCB->Flags    |= UCBF_SMSENABLED;
      cSec             = (UCHAR)cSec;
      npUCB->SecPerBlk = (cSec < MAX_MULTMODE_BLK) ? cSec : MAX_MULTMODE_BLK;
      rc = FALSE;       // multiple mode is ok to use
   }

   return( rc );
}


//
//   LBAModeEnable()
//
// Determine if this drive supports Logical Block Addressing, if
// not then the driver will always convert the input Relative Block
// Address to a Cylinder, Head, Sector address using the logical
// geometry.
//
BOOL NEAR LBAModeEnable( NPACB          npACB,
                         NPUCB          npUCB,
                         NPUTBL         npUT,
                         NPIDENTIFYDATA npID )
{
   BOOL   rc = TRUE;       // default to not enabled

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

   return( rc );
}


/*------------------------------------*/
/*                                    */
/* CalcONTRACKGeometry()              */
/*                                    */
/*------------------------------------*/
USHORT NEAR CalcONTRACKGeometry( NPUTBL npUT, NPUCB npUCB )
{
  NPGEOMETRY    npGEO;

  ULONG  ulTotalCylindersIn;                  /*@V117435*/
  USHORT usNumHeadsIn;                        /*@V117435*/
  USHORT usSectorsPerTrackIn;                 /*@V117435*/
                                              /*@V117435*/
  USHORT usNumHeadsTemp;                      /*@V117435*/
  USHORT usSectorsPerCylinder;                /*@V117435*/
                                              /*@V117435*/
  ULONG  ulTotalSectorsOut;                   /*@V117435*/
  ULONG  ulTotalCylindersOut;                 /*@V117435*/
  USHORT usNumHeadsOut;                       /*@V117435*/
  USHORT usSectorsPerTrackOut;                /*@V117435*/

  /*-------------------------------------------*/
  /* 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->IDEPhysGeom;

    /*---------------------------------------------------*/
    /* 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;

      ulTotalCylindersIn  = npGEO->TotalCylinders;                                  /*@V117435*/
      usNumHeadsIn        = npGEO->NumHeads;                                        /*@V117435*/
      usSectorsPerTrackIn = npGEO->SectorsPerTrack;                                 /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                             *@V117435
      ** Calculate output geometry.  Ontrack sectors per track is a constant.        *@V117435
      */                                                                            /*@V117435*/
      usSectorsPerTrackOut = ONTRACK_SECTORS_PER_TRACK;                             /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                              @V117435
      ** The maximum number of Ontrack cylinders is:                                  @V117435
      **   1024 Cylinders * 255 Heads * 63 Sectors = 16,450,560                       @V117435
      **                                           = 0x00fb0400 Total Sectors         @V117435
      **                                                                              @V117435
      ** Calculate total number of sectors, limited by the Ontrack maximum.           @V117435
      */                                                                            /*@V117435*/
      ulTotalSectorsOut = ulTotalCylindersIn * usNumHeadsIn * usSectorsPerTrackIn;  /*@V117435*/
      if( ulTotalSectorsOut > MAX_ONTRACK_SECTORS)                                  /*@V117435*/
        ulTotalSectorsOut = MAX_ONTRACK_SECTORS;                                    /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                              @V117435
      ** Compute the number Ontrack heads.  Add an extra Head if there are            @V117435
      ** any odd sectors.                                                             @V117435
      */                                                                            /*@V117435*/
      usNumHeadsOut = ulTotalSectorsOut / MAX_ONTRACK_SECTORS_PER_HEAD;             /*@V117435*/
      if( ulTotalSectorsOut % MAX_ONTRACK_SECTORS_PER_HEAD ) usNumHeadsOut++;       /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                              @V117435
      ** Round the number of heads to nearest power of 2 larger than the              @V117435
      ** number of heads calculated above.  Must have at least                        @V117435
      ** MIN_ONTRACK_HEADS and no more than MAX_ONTRACK_HEADS.                        @V117435
      */                                                                            /*@V117435*/
      if( usNumHeadsOut > 0x80 )                                                    /*@V117435*/
      {                                                                             /*@V117435*/
        usNumHeadsTemp = MAX_ONTRACK_HEADS;                                         /*@V117435*/
      }                                                                             /*@V117435*/
      else                                                                          /*@V117435*/
      {                                                                             /*@V117435*/
        for( usNumHeadsTemp = MIN_ONTRACK_HEADS; usNumHeadsTemp < usNumHeadsOut; )  /*@V117435*/
          usNumHeadsTemp = usNumHeadsTemp << 1;                                     /*@V117435*/
      }                                                                             /*@V117435*/
      usNumHeadsOut = usNumHeadsTemp;                                               /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                              @V117435
      ** Calculate Ontrack cylinders.                                                 @V117435
      */                                                                            /*@V117435*/
      usSectorsPerCylinder = usNumHeadsOut * usSectorsPerTrackOut;                  /*@V117435*/
      ulTotalCylindersOut = ulTotalSectorsOut / usSectorsPerCylinder;               /*@V117435*/
                                                                                    /*@V117435*/
      /*                                                                              @V117435
      ** Overlay calculated values back into the Geometry structure.                  @V117435
      */                                                                            /*@V117435*/
      npGEO->TotalSectors    = ulTotalSectorsOut;                                   /*@V117435*/
      npGEO->NumHeads        = usNumHeadsOut;                                       /*@V117435*/
      npGEO->TotalCylinders  = ulTotalCylindersOut;                                 /*@V117435*/
      npGEO->SectorsPerTrack = usSectorsPerTrackOut;                                /*@V117435*/
    }

    npUT->I13PhysGeom = npUCB->PhysGeom;
  }

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


/*------------------------------------*/
/*                                    */
/* DriveTypeToGeometry()              */
/*                                    */
/*------------------------------------*/
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 );
}
