/*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.      */
/*                                                                           */
/*****************************************************************************/
 /*static char *SCCSID = "%w% %e%";*/
#define SCCSID  "%w% %e%"
#define DEBUG 0
/**************************************************************************
 *
 * SOURCE FILE NAME = DMINIT.C
 *
 * DESCRIPTIVE NAME = OS2DASD.DMD - OS/2 DASD Device Manager
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Initialization routines for OS/2 DASD Device Mgr
 *
 *
 *
*/

/* @V111573 #include "infoseg.h" */  /* Now included in dmh.h */
#include "dmh.h"
#include "devclass.h"
#include "dskinit.h"

VOID   NEAR Build_UnitCBs (void);
VOID   NEAR Build_VolCBs (void);
VOID   NEAR InitDCS_VCS (void);
VOID   NEAR InitCBPool (void);
VOID   FAR  InitPost (PIORBH);
VOID   NEAR Setup_Partition_VolCBs (NPVOLCB, USHORT, BOOL, USHORT);     /*@V170941*/
USHORT NEAR Build_Next_VolCB (NPVOLCB, ULONG);                          /*@V170941*/
USHORT NEAR Reserve_Next_VolCB(NPVOLCB pPhysVolCB);                     /*@V187707*/
USHORT NEAR Setup_Extended_Volumes (NPVOLCB, USHORT, USHORT);           /*@V170941*/
USHORT NEAR Read_Sector (NPVOLCB, ULONG);
VOID   NEAR Init_Trace (void);
VOID   NEAR GetInitParms (PRPINITIN);
USHORT NEAR IsTraceNeeded(VOID);
VOID   NEAR InitRMInfo(VOID);
VOID   NEAR InitLogData(VOID);   /* @V111573 */
VOID NEAR memset( PSZ d, USHORT Value, USHORT n );

/* @V111573 typedef struct InfoSefGDT FAR *PInfoSegGDT; */ /*Now defined in dmdefs.h*/

GEOMETRY   DeviceGeo={196608,512,0,12,512,32};                          /*@V170941*/
//GEOMETRY   DeviceGeo={2090976,512,0,64,1021,32};                          /*@V170941*/
PARTITIONENTRY   PrimaryPart= {0x80,1,1,0,6,11,32,510,32,196192};       /*@V170941*/
//PARTITIONENTRY   PrimaryPart= {0x80,1,1,0,6,63,32,1020,32,2086912};       /*@V170941*/

/*--------------------------------------------------------------*/
/* Init data allocated at the end of the data segment.          */
/*--------------------------------------------------------------*/


USHORT          InitData=0;
UCHAR           InitTimeIORB[MAX_IORB_SIZE]={0};       /* Init time IORB   */
RP_RWV          InitTimeRP={0};                        /* Init time RP     */
PARTITIONTABLE  PartitionTables[MAX_FIXED_DISKS]={0};  /* Partition tables */
UCHAR           ReserveVols[MAX_FIXED_DISKS]={0};      /* /MP reserved partitions */ /*@V187707*/
UCHAR           Default_ReserveVols = 0;               /* /MP default */ /*@V187707*/

/* Perfview init time data */
USHORT          PerfViewInstalled = 0;                               /*@V81576*/

#define NumTimerCounters  7                                          /*@V53225*/

UCHAR    GroupName[] = "DISK 01";
UCHAR    str1[] = "ctRD";        /* Read Counter       */
UCHAR    str2[] = "qlRD";        /* Read Queue Counter */            
UCHAR    str3[] = "blRD";        /* Read Block Count   */
UCHAR    str4[] = "ctWR";        /* Write Counter      */
UCHAR    str5[] = "qlWR";         /* Write Queue Counters */         
UCHAR    str6[] = "blWR";        /* Write Block Count  */
UCHAR    str7[] = "tmBUSY";      /* Write Block Count  */            /*@V53225*/


TBN      NameBlock[]  =
{
   {PVW_CT_CNT, sizeof(CNT), 0 , str1},
   {PVW_CT_QLEN, sizeof(QLEN),0, str2},                              
   {PVW_CT_CNT, sizeof(CNT), 0 , str3},
   {PVW_CT_CNT, sizeof(CNT), 0 , str4},
   {PVW_CT_QLEN, sizeof(QLEN), 0 , str5},                            
   {PVW_CT_CNT, sizeof(CNT), 0 , str6},
   {PVW_CT_TIMR, sizeof(TIMR), 0 , str7}                             
};

TBH PerfViewTB = {
   TBH_VER_2_0_0_0,             /* Version Number */
   0,0,                         /* Block Instance ID, Block Group ID */
   0,0,0,GroupName,             /* Text Block Group Name */
   0,0,0,0,                     /* Text Block Instance Name */
   0,                           /* Message File Name */
   0,                           /* Help file name */
   NumTimerCounters,            /* Number of Timers + Counters */
   &NameBlock[0],               /* Pointer to array of Name Blocks */
};

#define MAX_DT_ADAPTERS  8
#define MAX_DT_UNITS     56
#define MAX_DT_SIZE (sizeof(DEVICETABLE) + ((MAX_DT_ADAPTERS-1) * 2) +        \
              (MAX_DT_ADAPTERS * (sizeof(ADAPTERINFO)-sizeof(UNITINFO))) +    \
              (MAX_DT_UNITS * sizeof(UNITINFO))  )

UCHAR           ScratchBuffer2[MAX_DT_SIZE]={0};       /*Scratch buffer */

/*-----------------------------------------------------*/
/* DriveInit  - Initialization for OS2DASD.DMD         */
/*-----------------------------------------------------*/
USHORT near DriveInit(pRP, pVolCB)

PRPINITIN  pRP;
NPVOLCB    pVolCB;
{
  USHORT     rc, i, NumBPBArrayEntries, NumExtraVolCBs;
  NPVOLCB    pVolCBx;
  PRPINITOUT pRPO = (PRPINITOUT) pRP;   /* Output for Init RP           */
  PVOID      pScratchBuffer;
  PUCHAR     p;                         // d159983

  extern USHORT (near *Strat1Near[])();
  extern struct SysDev3 near DiskDDHeader1,DiskDDHeader;

  DiskDDHeader.SysDevBef3.SDevNext=(ULONG)((PBYTE)(&DiskDDHeader1));     /* Pointer to next device header */
  p=(PUCHAR)DriveInit;
  DiskDDHeader1.SysDevBef3.SDevProtCS=SELECTOROF(p);

  /* Initialize various variables */

  DDFlags |= DDF_INIT_TIME;            /* Turn off init time flag */

  Device_Help = pRP->DevHlpEP;         /* Save ptr to devhelp function   */

  pDataSeg = (PVOID) &pDataSeg;        /* Set up pointer to data segment */
  OFFSETOF(pDataSeg) = 0;

  InitLogData();  /* @V111573 */

  /* Get the init parms specified on the BASEDEV= command line */

  GetInitParms(pRP);

  rc = DevHelp_GetDOSVar( 13, 0, &p);   // d159983
  pInterruptLevel = p;                  // d159983


  /* Save away the physical and linear addresses of our data segment */

  rc = DevHelp_VirtToPhys(pDataSeg, (PULONG) &ppDataSeg);

  rc = DevHelp_VirtToLin((USHORT) (SELECTOROF(pDataSeg)),
                         (ULONG) (OFFSETOF(pDataSeg)),
                         (PLIN) &plDataSeg);  /* Save lin addr of data seg    */

  /* Save away the physical address of the scratch read buffer      */

  SELECTOROF(pScratchBuffer) = SELECTOROF(pDataSeg);
  OFFSETOF(pScratchBuffer) = (USHORT) &ScratchBuffer[0];

  rc = DevHelp_VirtToPhys(pScratchBuffer, (PULONG) &ppScratchBuffer);

  RMCreateDriver(&RMDASDDesc, &hRMOS2DASD );

  /* Put init data at end of _DATA since it'll get discarded later     */

  PoolSize = INIT_POOL_SIZE;

  FreePoolSpace = INIT_POOL_SIZE;

  Build_UnitCBs();                      /* Build unit control blocks   */


  if (NumUnitCBs == 0)                  /* Handle no_media case */
  {
     /* Setup the return parameters for the no media case */

     DDFlags |= DDF_NO_MEDIA;
     pRPO->Unit = 2;                                                 /*@V63867*/

     pRPO->BPBArray = (PVOID) InitBPBArray;

     InitBPBArray[0] = &(BPB_144MB);
     InitBPBArray[1] = &(BPB_144MB);

     pRPO->CodeEnd = (USHORT) DriveInit;
     pRPO->DataEnd = (USHORT) CBPool;
  }
  else  /* Media exists which is the typical case */
  {
     Build_VolCBs();                    /* Build Volume control blocks */

     InitDCS_VCS();                     /* Initialize the DCS and VCS strucs */

     InitCBPool ();                     /* Initialize the Control Block pool */

     Init_Trace();                      /* Initialize tracing */

     /* Set up the return parameters for the INIT packet. */

     NumLogDrives = NumRemovableDisks + NumPartitions;

     if (pVolCB_DriveA->Flags & vf_AmMult)
        NumLogDrives++;                    /* Add the psuedo drive */
     else if (NumRemovableDisks == 0)
        NumLogDrives += 2;                 /* No floppy case */
     else if (pVolCB_DriveA->PhysDriveNum == -1)
        NumLogDrives += 2;                 /* Removable, but no A: or B: */

     pRP->Unit = (CHAR) NumLogDrives;      /* Return number of logical drives */


     /* Setup the return BPB array */

     NumExtraVolCBs = NumRemovableDisks;

     if (NumRemovableDisks == 0)
        NumExtraVolCBs++;

     NumBPBArrayEntries = NumLogDrives + NumExtraVolCBs;

     if (NumBPBArrayEntries > MAX_DRIVE_LETTERS)
       NumBPBArrayEntries = MAX_DRIVE_LETTERS;

     pRPO->BPBArray = (PVOID) InitBPBArray;

     InitBPBArray[0] = &(pVolCB_DriveA->MediaBPB);
     InitBPBArray[1] = &(pVolCB_DriveB->MediaBPB);

     pVolCBx = pVolCB_DriveC;
     for (i = 2; i < NumLogDrives; i++, pVolCBx = pVolCBx->pNextVolCB)
        InitBPBArray[i] = &(pVolCBx->MediaBPB);

     /* Return the end of the code and data segments */

     pRPO->DataEnd =  (USHORT) pNextFreeCB;

     pRPO->CodeEnd = (DDFlags & DDF_DISCARD_TRACE)   ? (USHORT) IsTraceNeeded /*@V81586*/
                                                     : (USHORT) DriveInit;    /*@V81586*/
  }

  InitRMInfo();

  /* Dont allow another INIT command to come in */

  Strat1Near[CMDInitBase] = CmdErr;   /* Patch strat1 table to disable inits */
  DDFlags &= ~DDF_INIT_TIME;          /* Turn off init time flag */

  return(STDON);                      /* Done with init, so return */

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME: Build_UnitCBs                                            *
*                                                                           *
* DESCRIPTIVE NAME: Build Unit Control Blocks  (UnitCBs)                    *
*                                                                           *
* FUNCTION:  This routine issues the GetAdapterDeviceTable command          *
*            to each Adapter Device Driver and builds the unit control      *
*            blocks from each Adapter Device Table returned.  Each unit     *
*            control block (UnitCB) represents a physical device unit       *
*            (i.e. drive numbers 0x80,0x81, etc.) which the adapter         *
*            device driver manages.                                         *
*                                                                           *
* ENTRY POINT: Build_UnitCBs                                                *
*                                                                           *
* LINKAGE: Call Near                                                        *
*                                                                           *
* INPUT:                                                                    *
*                                                                           *
* EXIT-NORMAL: UnitCBs built for each physical unit.                        *
*              NumUnitCBs = Number of UnitCBs built                         *
*                                                                           *
* EXIT-ERROR: None                                                          *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

void Build_UnitCBs()

{
  USHORT                rc;
  NPUNITCB              pUnitCB;         /* Pointer to current UnitCB    */
  NPIORB_CONFIGURATION  pIORB;           /* ptr to IORB                  */
  DEVICETABLE           *pDeviceTable;   /* ptr to device table          */
  NPADAPTERINFO         pAdapterInfo;    /* near ptr to AdapterInfo      */
  USHORT                i, j, k;         /* Index pointers               */
  USHORT                FilterADDHandle; /* Filter Handle                */
  struct DevClassTableStruc far *pDriverTable;  /*  ptr to registered ADD EPs*/
  VOID (FAR * DriverEP) ();                  /* Driver entry point           */
  VOID (FAR * DriverEPF)();             /* Filter Driver entry point */ /*@V736405*/

  (NPUNITCB)pNextFreeCB = FirstUnitCB;  /* Next free is first unit CB   */
  UnitCB_Head = FirstUnitCB;            /* Init UnitCB head pointer     */
  pUnitCB = UnitCB_Head;                /* Point to first UnitCB        */
  NumUnitCBs = 0;                       /* Init UnitCB count            */

  /*--------------------------------------------------------------------*/
  /* Get the adapter device tables for each adapter driver and create   */
  /* the unit control blocks (UnitCBs) from the returned tables.        */
  /*--------------------------------------------------------------------*/

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

  NumDrivers = pDriverTable->DCCount;

  pDeviceTable = (DEVICETABLE *) ScratchBuffer2;

  for (i = 0; i < NumDrivers; i++)
  {
     pIORB = (NPIORB_CONFIGURATION) InitTimeIORB;
     pIORB->iorbh.Length = sizeof(IORB_CONFIGURATION);
     pIORB->iorbh.CommandCode = IOCC_CONFIGURATION;
     pIORB->iorbh.CommandModifier = IOCM_GET_DEVICE_TABLE;
     pIORB->iorbh.Status = 0;
     pIORB->iorbh.ErrorCode = 0;                                     /*@V56638*/
     pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
     pIORB->iorbh.NotifyAddress = &InitPost;
     pIORB->pDeviceTable = (PVOID) pDeviceTable;
     pIORB->DeviceTableLen = sizeof(ScratchBuffer2);

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

     f_ZeroCB((PBYTE)pDeviceTable, sizeof(ScratchBuffer2));

     (*DriverEP) ((PVOID)(pIORB));                 // Call driver to fill out Device Table

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

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

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

           /* Only allocate DISK type devices which are not defective */
           /* and which dont suppress DASD manager support.           */

           if ( (
                 (pAdapterInfo->UnitInfo[k].UnitType == UIB_TYPE_DISK) ||                            /*@S170941*/
                 (OpticalAsFixed && (pAdapterInfo->UnitInfo[k].UnitType == UIB_TYPE_OPTICAL_MEMORY)) /*@S170941*/
                ) &&                                                                                 /*@S170941*/
                ! (pAdapterInfo->UnitInfo[k].UnitFlags &
                        (UF_NODASD_SUPT | UF_DEFECTIVE)) )
           {
              /* Allocate the unit if it's not already allocated */
              /* Wait until the request comes back from ADD.     */

              pIORB->iorbh.Length = sizeof(IORB_UNIT_CONTROL);
              pIORB->iorbh.CommandCode = IOCC_UNIT_CONTROL;
              pIORB->iorbh.CommandModifier = IOCM_ALLOCATE_UNIT;
              pIORB->iorbh.Status = 0;
              pIORB->iorbh.ErrorCode = 0;                            /*@V56638*/
              pIORB->iorbh.UnitHandle = pAdapterInfo->UnitInfo[k].UnitHandle;
              pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
              pIORB->iorbh.NotifyAddress = &InitPost;
              ((NPIORB_UNIT_CONTROL)pIORB)->Flags = 0;
              ((NPIORB_UNIT_CONTROL)pIORB)->pUnitInfo = 0;
              ((NPIORB_UNIT_CONTROL)pIORB)->UnitInfoLen = 0;
                                                                    /*@V736405*/
              FilterADDHandle = pAdapterInfo->UnitInfo[k].FilterADDHandle;
                                                                    /*@V736405*/
              if ( !FilterADDHandle )                               /*@V736405*/
              {
                 (*DriverEP) ((PVOID)(pIORB));
              }                                                     /*@V736405*/
              else                                                  /*@V736405*/
              {                                                     /*@V736405*/
                 OFFSETOF(DriverEPF) =                              /*@V736405*/
                      pDriverTable->DCTableEntries[FilterADDHandle-1].DCOffset;
                 SELECTOROF(DriverEPF) =                            /*@V736405*/
                      pDriverTable->DCTableEntries[FilterADDHandle-1].DCSelector;
                                                                    /*@V736405*/
                 (*DriverEPF) ((PVOID)(pIORB));                     /*@V736405*/
              }                                                     /*@V736405*/

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

              /* If allocation succeeded then add unit to unit tables */

              if ( !(pIORB->iorbh.Status & IORB_ERROR) )
              {
                 NumUnitCBs ++;                        /* update unit count */
                 pUnitCB->UnitInfo = pAdapterInfo->UnitInfo[k];

                 /* Save the callable entry point of the adapter driver. If */
                 /* the unit is being filtered, use the entry point of the  */
                 /* filter driver.                                          */


                 if (FilterADDHandle == 0)
                 {
                     pUnitCB->AdapterDriverEP = DriverEP;
                     pUnitCB->ADDHandle       = i+1;
                 }
                 else
                 {                                                  /*@V736405*/
                    pUnitCB->ADDHandle = FilterADDHandle;

                    OFFSETOF(pUnitCB->AdapterDriverEP)   = OFFSETOF(DriverEPF);
                    SELECTOROF(pUnitCB->AdapterDriverEP) = SELECTOROF(DriverEPF);
                 }

                 pUnitCB->AdapterNumber = NumAdapters + j;

                 pUnitCB->MaxHWSGList = pAdapterInfo->MaxHWSGList;

                 if (pAdapterInfo->AdapterFlags & AF_HW_SCATGAT)
                    pUnitCB->Flags |= UCF_HW_SCATGAT;

                 if (pAdapterInfo->AdapterFlags & AF_16M)
                    pUnitCB->Flags |= UCF_16M;

                 if (pAdapterInfo->AdapterFlags & AF_CHS_ADDRESSING)
                    pUnitCB->Flags |= UCF_CHS_ADDRESSING;
                 /*
                 ** Set the bus interface type.
                 */
                 if ((pAdapterInfo->AdapterDevBus & 0x00FF) ==
                                                AI_DEVBUS_ST506)
                    pUnitCB->InterfaceType = INTERFACE_ATAPI;
                 else
                    pUnitCB->InterfaceType = INTERFACE_SCSI;

                 pUnitCB->SortMethod  = DefaultSortMethod;              /*@V74404*/
                 pUnitCB->QueueMethod = DefaultQueueMethod;             /*@V74404*/
                 pUnitCB->QueueDepth  = pUnitCB->UnitInfo.QueuingCount; /*@V74404*/

                 if ((pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) ||            /*@S170941*/
                     (OpticalAsFixed && (pAdapterInfo->UnitInfo[k].UnitType == UIB_TYPE_OPTICAL_MEMORY))
                    )
                 {
                    pUnitCB->PhysDriveNum = NumRemovableDisks;
                    NumRemovableDisks++;              //HCT Tally non floppy removable
                    if ((pAdapterInfo->AdapterDevBus & 0x00FF)
                                                       != AI_DEVBUS_FLOPPY)
                       {
                       pUnitCB->Flags |= UCF_REMOVABLE_NON_FLOPPY;

                       /* If treating removables as fixed AND
                        *    Unit has not requested to be treated as large floppy
                        * Then
                        *    Mark unit as Partitioned Removable (UCF_REMOVABLE_AS_FIXED)
                        */
                       if (TreatAsFixed  &&                                     /*@S170941*/
                           !(pUnitCB->UnitInfo.UnitFlags & UF_LARGE_FLOPPY)     /*@V187707*/
                          )                                                     /*@V187707*/
                         {                                                      /*@S170941*/

                         // reset the drive number                              /*@S170941*/
                         pUnitCB->PhysDriveNum = 0;                             /*@S170941*/


                         // don't count the drive as removable                  /*@S170941*/
                         NumRemovableDisks--;                                   /*@S170941*/

                         NumRMasFixed++;     //HCT Debug - Tally removable treated as fixed

                         // remove the removable bit                            /*@S170941*/
                         pUnitCB->UnitInfo.UnitFlags &=~UF_REMOVABLE;           /*@S170941*/
                         // indicate its removable, but treated as fixed        /*@S170941*/
                         pUnitCB->Flags |=UCF_REMOVABLE_AS_FIXED;               /*@S170941*/
                         // mark it as disk type                                /*@S170941*/
                         pUnitCB->UnitInfo.UnitType = UIB_TYPE_DISK;            /*@S170941*/
                         } /* endif */                                          /*@S170941*/
                       }
                 }
                 else
                 {
                    pUnitCB->PhysDriveNum = NumFixedDisks + 0x80;
                    NumFixedDisks++;
                 }
                 pUnitCB->pNextUnitCB = pUnitCB + 1;
                 pUnitCB++;
              }
             //HCT if allocation fail, ignore ?
           }
        }  /* end unit loop */



     }  /* end adapter loop */
     NumAdapters += pDeviceTable->TotalAdapters;
  }  /* end driver loop */

  (NPUNITCB) pNextFreeCB = pUnitCB;     /* Update next free control blk ptr */
  (pUnitCB-1)->pNextUnitCB = 0;
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME: Build_VolCBs                                             *
*                                                                           *
* DESCRIPTIVE NAME: Build Volume Control Blocks  (VolCBs)                   *
*                                                                           *
* FUNCTION:  This routine builds the physical and logical volume            *
*            control blocks for each volume.                                *
*                                                                           *
* ENTRY POINT: Build_VolCBs                                                 *
*                                                                           *
* LINKAGE: Call Near                                                        *
*                                                                           *
* INPUT:                                                                    *
*                                                                           *
* EXIT-NORMAL: VolCBs built for each volume.                                *
*              NumVolCBs = Number of VolCBs built                           *
*                                                                           *
* EXIT-ERROR: None                                                          *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

void Build_VolCBs()

{
  NPVOLCB         pVolCB;
  NPVOLCB         pPseudoFixed;
  NPUNITCB        pUnitCB;
  USHORT          iUnit;
  USHORT          iVol;
  USHORT          NumPseudoFixed;
  NPIORB_GEOMETRY pIORB;                /* ptr to IORB                     */

  BOOL A_Found = FALSE;                 /* A: drive found                  */
  BOOL B_Found = FALSE;                 /* B: drive found                  */
  UCHAR PseudoB = NO;
  USHORT ReChain = NO;


  /*-----------------------------------------------*/
  /* Create Volume Controls Blocks for units       */
  /* managing A: and B:                            */
  /*-----------------------------------------------*/

  /* The VolCB chain starts with Drive A: and Drive B:  */
  /* and are placed right after the UnitCBs             */

  VolCB_Head = (NPVOLCB)pNextFreeCB;

  pVolCB_DriveA = VolCB_Head;
  pVolCB_DriveA->LogDriveNum = 0;          /* LogDriveNum for A: is 0  */
  pVolCB_DriveA->pNextVolCB = pVolCB_DriveA + 1;

  pVolCB_DriveB = VolCB_Head + 1;
  pVolCB_DriveB->LogDriveNum = 1;       /* LogDriveNum for B: is 1  */
  pVolCB_DriveB->pNextVolCB = pVolCB_DriveB + 1;


  pUnitCB = UnitCB_Head;                /* Point back to head UnitCB       */
  for (iUnit=0; iUnit < NumUnitCBs; iUnit++, pUnitCB++)
  {
     if (A_Found && B_Found)
        break;

     if ((pUnitCB->UnitInfo.UnitFlags & UF_A_DRIVE) && !(A_Found))
     {
        A_Found = TRUE;                          /* Indicate A: found        */
        pVolCB_DriveA->pUnitCB = pUnitCB;        /* Link VolCB to UnitCB     */
        pVolCB_DriveA->PhysDriveNum = 0;         /* PhysDriveNum for A: is 0 */
        pVolCB_DriveA->Flags |= vf_OwnPhysical;  /* Owns physical drive      */
        pUnitCB->pCurrentVolCB = pVolCB_DriveA;

        if ((pUnitCB->UnitInfo.UnitFlags & UF_B_DRIVE) && !(B_Found))
        {
           B_Found = TRUE;                       /* Indicate B: found   */
           PseudoB = YES;                        /* Indicate Pseudo B drive */
        }
     }
     else if ((pUnitCB->UnitInfo.UnitFlags & UF_B_DRIVE) && !(B_Found))
     {
        B_Found = TRUE;                          /* Indicate B: found        */
        pVolCB_DriveB->pUnitCB = pUnitCB;        /* Link VolCB to UnitCB     */
        pVolCB_DriveB->PhysDriveNum = 1;         /* PhysDriveNum for B: is 1 */
        pVolCB_DriveB->Flags |= vf_OwnPhysical;  /* Owns physical drive      */
        pUnitCB->pCurrentVolCB = pVolCB_DriveB;
     }
  }

  /* If PseudoB drive found, or found A: but not B: */

  if ( (PseudoB == YES) || (A_Found && !B_Found) )
  {
     PseudoB = YES;                                                  /*@V50363*/
     pVolCB_DriveA->Flags |= vf_AmMult;      /* Mult VolCBs mapped to unit  */
     pVolCB_DriveB->Flags |= vf_AmMult;      /* Mult VolCBs mapped to unit */
     pVolCB_DriveB->pUnitCB = pVolCB_DriveA->pUnitCB;
     pVolCB_DriveB->PhysDriveNum = 0;
  }

  /* If found B:, but not A: */

  else if (!A_Found && B_Found)
  {
     pVolCB_DriveA->PhysDriveNum = 0;
     pVolCB_DriveA->pUnitCB = pVolCB_DriveB->pUnitCB;
     pVolCB_DriveA->Flags |= vf_AmMult + vf_OwnPhysical;
     pVolCB_DriveA->pUnitCB->pCurrentVolCB = pVolCB_DriveA;

     pVolCB_DriveB->PhysDriveNum = 0;
     pVolCB_DriveB->Flags |= vf_AmMult;      /* Mult VolCBs mapped to unit */
     pVolCB_DriveB->Flags & ~vf_OwnPhysical;
  }

  /* If no A: or B:           */

  else if (!A_Found && !B_Found)
  {
     pVolCB_DriveA->PhysDriveNum = -1;
     pVolCB_DriveA->LogDriveNum = -1;
     pVolCB_DriveA->MediaBPB = BPB_144MB;

     pVolCB_DriveB->PhysDriveNum = -1;
     pVolCB_DriveB->LogDriveNum = -1;
     pVolCB_DriveB->MediaBPB = BPB_144MB;
  }


  NumVolCBs = 2;
  pVolCB = VolCB_Head + 2;

  /*-----------------------------------------------*/
  /* Create volume control blocks for removable    */
  /* disk devices which were not assigned A: or B: */
  /*-----------------------------------------------*/


  for (iUnit=0, pUnitCB = UnitCB_Head; iUnit < NumUnitCBs; iUnit++, pUnitCB++)
  {
     if ( (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) &&
         pUnitCB->UnitInfo.UnitType == UIB_TYPE_DISK &&
         !(pUnitCB->UnitInfo.UnitFlags & (UF_A_DRIVE | UF_B_DRIVE) ) )
     {
        pVolCB->pUnitCB = pUnitCB;
        pVolCB->LogDriveNum = -1;
        pVolCB->PhysDriveNum = pUnitCB->PhysDriveNum;
        pVolCB->Flags |= vf_OwnPhysical;         /* Owns physical drive      */
        pUnitCB->pCurrentVolCB = pVolCB;
        pVolCB->pNextVolCB = pVolCB + 1;
        pVolCB++;
        NumVolCBs++;
     }
  }

  pLastLogVolCB = pVolCB-1;                                                       /*@S170941*/

  /*------------------------------------------------*/
  /* Create volume control blocks for non-removable */
  /* disk devices.                                  */
  /*------------------------------------------------*/

  pVolCB_80 = pVolCB;
  for (iUnit=0, pUnitCB = UnitCB_Head; iUnit < NumUnitCBs; iUnit++, pUnitCB++)
  {
     if ( !(pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) &&     // don't count removable
         !(pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED) &&         // don't count removable as fixed
         pUnitCB->UnitInfo.UnitType == UIB_TYPE_DISK)
     {
        pVolCB->pUnitCB = pUnitCB;
        pVolCB->LogDriveNum = pUnitCB->PhysDriveNum;  /* Assign Logical same */
        pVolCB->PhysDriveNum = pUnitCB->PhysDriveNum;  /*  as physical       */
        pVolCB->Flags |= vf_OwnPhysical;         /* Owns physical drive      */
        pUnitCB->pCurrentVolCB = pVolCB;
        pVolCB->pNextVolCB = pVolCB + 1;
        pVolCB++;
        NumVolCBs++;
     }
   }

  pPseudoFixed=pVolCB;                                                            /*@S170941*/
  NumPseudoFixed=0;        // Removable as fixed                                  /*@S170941*/

  if(TreatAsFixed)         // if flag & UCF_REMOVABLE_AS_FIXED          /*@S170941*/
  {                                                                     /*@S170941*/
    for (iUnit=0, pUnitCB = UnitCB_Head; iUnit < NumUnitCBs; iUnit++, pUnitCB++)/*@S170941*/
    {                                                                   /*@S170941*/
       if ( (pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED ) &&               /*@S170941*/
           pUnitCB->UnitInfo.UnitType == UIB_TYPE_DISK)                 /*@S170941*/
       {                                                                /*@S170941*/
          pVolCB->pUnitCB = pUnitCB;                                    /*@S170941*/
          pVolCB->LogDriveNum = pUnitCB->PhysDriveNum = NumFixedDisks+NumPseudoFixed+0x80;  /*@S170941*/
          pVolCB->PhysDriveNum = pUnitCB->PhysDriveNum;  /*  as physical       */ /*@S170941*/
          pVolCB->Flags |= vf_OwnPhysical;         /* Owns physical drive      */ /*@S170941*/
          pUnitCB->pCurrentVolCB = pVolCB;                                        /*@S170941*/
          pVolCB->pNextVolCB = pVolCB + 1;                                        /*@S170941*/
          pVolCB++;                                                               /*@S170941*/
          NumVolCBs++;                                                            /*@S170941*/
          NumPseudoFixed++;                                                       /*@S170941*/
       }                                                                          /*@S170941*/
    }                                                                             /*@S170941*/
  } /* endif */                                                                   /*@S170941*/

  (pVolCB-1)->pNextVolCB = (NPVOLCB) NULL;          /* Terminate VolCB chain */


  /*------------------------------------------------*/
  /* We now have all the physical VolCB's created.  */
  /* Fill in the BPB and other various device       */
  /* parameters in the VolCB.                       */
  /*------------------------------------------------*/

  pIORB = (NPIORB_GEOMETRY) InitTimeIORB;

  for (iVol = 0, pVolCB = VolCB_Head; iVol < NumVolCBs; iVol++, pVolCB++)
  {
     if (pVolCB->Flags & vf_OwnPhysical)
     {
        pIORB->iorbh.Length = sizeof(IORB_GEOMETRY);
        pIORB->iorbh.CommandCode = IOCC_GEOMETRY;
        pIORB->iorbh.CommandModifier = IOCM_GET_DEVICE_GEOMETRY;
        pIORB->iorbh.UnitHandle = pVolCB->pUnitCB->UnitInfo.UnitHandle;
        pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
        pIORB->iorbh.Status = 0;
        pIORB->iorbh.ErrorCode = 0;                                  /*@V58430*/
        pIORB->iorbh.NotifyAddress = &InitPost;
        pIORB->pGeometry = (GEOMETRY *) ScratchBuffer2;
        pIORB->GeometryLen = sizeof(struct _GEOMETRY);

        f_ZeroCB((PBYTE)pIORB->pGeometry, pIORB->GeometryLen);

        pUnitCB = pVolCB->pUnitCB;

        //HCT  Call ADD to get geometry
        (*pUnitCB->AdapterDriverEP) ((PVOID) (pIORB));

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

        if (pIORB->iorbh.Status & IORB_ERROR)                        /*@V63867*/
        {                                                            /*@V63867*/
#if DEBUG
          Msginfo.MsgStrings[0]="Media Geometry Error";
          DevHelp_Save_Message((NPBYTE)&Msginfo);
#endif
          f_ZeroCB((PBYTE)pIORB->pGeometry, sizeof(GEOMETRY) );      /*@V63867*/

          if(pVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED)             /*@S170941*/
            {                                                             /*@S170941*/
            pVolCB->Flags |= vf_noDisk;                                   /*@S170941*/
            *(pIORB->pGeometry) = DeviceGeo;                              /*@S170941*/
            }                                                             /*@S170941*/
        }                                                            /*@V63867*/
        else
        {
          // if removable media to be treated as fixed, lock the media in if any   /*@S170941*/
          if(pVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED)             /*@S170941*/
          {                                                               /*@S170941*/

#if DEBUG
             Msginfo.MsgStrings[0]="Lock media in drive";
             DevHelp_Save_Message((NPBYTE)&Msginfo);
#endif
             pIORB->iorbh.Length = sizeof(IORB_DEVICE_CONTROL);           /*@S170941*/
             pIORB->iorbh.CommandCode = IOCC_DEVICE_CONTROL;              /*@S170941*/
             pIORB->iorbh.CommandModifier = IOCM_LOCK_MEDIA;              /*@S170941*/
             pIORB->iorbh.UnitHandle = pVolCB->pUnitCB->UnitInfo.UnitHandle;/*@S170941*/
             pIORB->iorbh.RequestControl = IORB_ASYNC_POST;               /*@S170941*/
             pIORB->iorbh.Status = 0;                                     /*@S170941*/
             pIORB->iorbh.ErrorCode = 0;                                  /*@S170941*/
             pIORB->iorbh.NotifyAddress = &InitPost;                      /*@S170941*/
                                                                          /*@S170941*/
             pUnitCB = pVolCB->pUnitCB;                                   /*@S170941*/
                                                                          /*@S170941*/
             //HCT  Call ADD to lock media
             (*pUnitCB->AdapterDriverEP) ((PVOID) (pIORB));               /*@S170941*/
                                                                          /*@S170941*/
             while ( !(pIORB->iorbh.Status & IORB_DONE) )  /* Wait till done *//*@S170941*/
             ;                                                            /*@S170941*/
          } /* endif */                                                   /*@S170941*/
        }

        f_BPBFromGeom( pVolCB, &(pVolCB->RecBPB), pIORB->pGeometry); /*@V63867*/

        pVolCB->MediaBPB = pVolCB->RecBPB;     /* Copy Rec BPB to media BPB */

        if(pVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED)             /*@S170941*/
          {                                                             /*@S170941*/
          pVolCB->Flags |= (vf_noDisk|vf_UncertainMedia);               /*@S170941*/
          }


        if (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)     
           pVolCB->MediaBPB.BytesPerSector = 1024;          
     }
  }

  /* Fill in the BPB for the Psuedo B: if there is one */

  if (PseudoB == YES)
  {
     pVolCB_DriveB->MediaBPB = pVolCB_DriveA->MediaBPB;
     pVolCB_DriveB->RecBPB = pVolCB_DriveA->RecBPB;
     pVolCB_DriveB->NumPhysCylinders = pVolCB_DriveA->NumPhysCylinders; /*@V59959*/
  }

  (NPVOLCB)pNextFreeCB = pVolCB;

  if (NumFixedDisks > 0  ||  NumPseudoFixed > 0)
  {
     NextLogDriveNum = 2;
     pVolCB_DriveC = (NPVOLCB)pNextFreeCB;
  }

  if (NumFixedDisks > 0)
     Setup_Partition_VolCBs(pVolCB_80,NumFixedDisks,FALSE, 0);         /* Setup VolCBs for partitions       */

  if(NumPseudoFixed>0)                                                       /*@S170941*/
     Setup_Partition_VolCBs(pPseudoFixed,NumPseudoFixed,TRUE,NumFixedDisks); /*@S170941*/

  /* Go back and fill in the logical drive number for those volumes         */
  /* we assign at the end (i.e. removable drives which are not A: or B:     */
  /* Also, rearrage the VolCB chain pointers to have these VolCBs           */
  /* chained after the last fixed disk logical VolCB.                       */

  pVolCB = VolCB_Head;
  ReChain = NO;
  for (iVol = 0; iVol < NumVolCBs; iVol++, pVolCB = pVolCB->pNextVolCB)
  {
     if ( (pVolCB->LogDriveNum == -1) && (pVolCB->PhysDriveNum != -1) )
     {
        ReChain = YES;
        pVolCB->LogDriveNum = NextLogDriveNum++;
     }
  }

  if (ReChain == YES)
  {
     pLastLogVolCB->pNextVolCB = pVolCB_DriveB->pNextVolCB;
     pVolCB_DriveB->pNextVolCB = pVolCB_DriveC;
     (pVolCB_80-1)->pNextVolCB = pVolCB_80;
  }

  /* Start d159983 */

  /*
   * For non-removable fixed disks, create quick lookup table.
   */

  pVolCB = VolCB_Head;
  for (iVol = 0; iVol < NumVolCBs; iVol++, pVolCB = pVolCB->pNextVolCB)
  {

     pUnitCB = pVolCB->pUnitCB;
     if (!(pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
         &&
         pUnitCB->UnitInfo.UnitType == UIB_TYPE_DISK)
     {
       DriveToVolCB[pVolCB->LogDriveNum] = pVolCB;
     }
  }

  /* End   d159983 */

  /* Reserve 1 extra VolCB for each removable unit so drive aliasing */
  /* via the DEVICE=EXTDSKDD command can be supported.               */

  if (NumRemovableDisks != 0)
  {
     pExtraVolCBs = (NPVOLCB) pNextFreeCB;
     NumExtraVolCBs = NumRemovableDisks;
     pNextFreeCB = pNextFreeCB + (NumRemovableDisks * sizeof(VOLCB));
  }
  NumFixedDisks += NumPseudoFixed;                                     /*@S170941*/

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME: Setup_Partition_VolCBs                                   *
*                                                                           *
* DESCRIPTIVE NAME: Installs a VolCB for each OS/2 partition found          *
*                   on every fixed disks.                                   *
*                                                                           *
* FUNCTION:  This routine searches all fixed disks (80H - 86H) looking      *
*            for a primary partition and extended volumes on each           *
*            disk.                                                          *
*                                                                           *
*            A VolCB is installed for every primary partition, then VolCBs  *   *
*            are installed for all extended volumes. If drive 80H did       *
*            not have a primary partition, it will not be searched for      *
*            extended volumes.  All following fixed drives will always      *
*            be searched for extended volumes whether it had a primary      *
*            partition or not.                                              *
*                                                                           *
* NOTES: The VolCBs for all physical fixed drives must be set up            *
*        and NumFixedDisks must be set to the number of fixed disks         *
*        prior to calling this routine.                                     *
*                                                                           *
* ENTRY POINT: Setup_Partition_VolCBs                                       *
*                                                                           *
* LINKAGE: Call Near                                                        *
*                                                                           *
* INPUT: None                                                               *
*                                                                           *
* EXIT-NORMAL: VolCBs installed for all partitions found                    *
*                                                                           *
* EXIT-ERROR: None                                                          *
*                                                                           *
* EFFECTS: Modifies NumPartitions                                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/


void near Setup_Partition_VolCBs(NPVOLCB pStartScan, USHORT Count, BOOL Linear, USHORT StartDiskIndex)/*@S170941*/

{
   NPVOLCB pPhysVolCB, pVolCB;
   ULONG   NumSectors;
   USHORT  iDisk, i,count=0;
   MBR     near *pMBR = (MBR near *) ScratchBuffer; /* Ptr to buffer for MBR */              /*@S170941*/
   NPVOLCB pLogVolCB = (NPVOLCB) pNextFreeCB;
   USHORT  rc;                                                       /*@V191357*/

   /*-----------------------------------------------*/
   /*  Allocate VolCBs for all Primary Partitions   */
   /*-----------------------------------------------*/


   for (iDisk=StartDiskIndex,pPhysVolCB=pStartScan; iDisk<StartDiskIndex+Count; iDisk++,pPhysVolCB++)/*@S170941*/
   {

      /*** added by WKP - start */
      NumSectors = pPhysVolCB->MediaBPB.NumHeads *
                   pPhysVolCB->MediaBPB.SectorsPerTrack *
                   pPhysVolCB->NumPhysCylinders;

      if (NumSectors > 0xFFFF)
      {
         pPhysVolCB->MediaBPB.BigTotalSectors = NumSectors;
         pPhysVolCB->MediaBPB.TotalSectors = 0;
         pPhysVolCB->RecBPB.BigTotalSectors = NumSectors;
         pPhysVolCB->RecBPB.TotalSectors = 0;
      }
      else
      {
         pPhysVolCB->MediaBPB.TotalSectors = (USHORT)NumSectors;
         pPhysVolCB->RecBPB.TotalSectors = (USHORT)NumSectors;
      }
      /*** added by WKP -end */

      /* Initialize count of number of partitions in this disk.
       * Only used when in 'Linear' mode.
       */
      count=0;                                                         /*@V187707*/

      /* Read the Master Boot Record (MBR) and copy the partition table */
      /* into a temporary buffer for later use.                         */

      if ((rc = Read_Sector(pPhysVolCB,0L)) == ERROR) /* Read MBR & check for error*/
         /* Removable media may fail the first read, so try again. */  /*@V191357*/
         if (pPhysVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED)      /*@V191357*/
            rc = Read_Sector(pPhysVolCB,0L);                           /*@V191357*/

      if (rc == ERROR)
      {                                                                /*@S170941*/

#if DEBUG
         Msginfo.MsgStrings[0]="can't read MBR";
         DevHelp_Save_Message((NPBYTE)&Msginfo);
#endif
         PartitionTables[iDisk].Bad_MBR=TRUE;                          /*@S170941*/
         PartitionTables[iDisk].No_PrimPart = TRUE;                    /*@V187707*/
      }                                                                /*@S170941*/
      else                                                             /*@S170941*/
      {                                                                /*@S170941*/

#if DEBUG
         Msginfo.MsgStrings[0]="process partition table";
         DevHelp_Save_Message((NPBYTE)&Msginfo);
#endif
         for (i = 0; i < 4; i++)
            PartitionTables[iDisk].PartitionTable[i]
                                             = pMBR->PartitionTable[i];

         if (Build_Next_VolCB(pPhysVolCB,0L) == ERROR)
            PartitionTables[iDisk].No_PrimPart = TRUE;                 /*@V88662*/
         else                                                          /*@S170941*/
            ++count;                    // count first partition       /*@S170941*/
      }

      if(Linear==TRUE)                                                 /*@S170941*/
      {                                                                /*@S170941*/
        /* For 'Linear' processing, build the extended partition       //@V187707
         * VolCBs for this disk immediately after its primary.
         * If this is partitioned removable media, reserve VolCBs
         * for this disk, as specified by the /MP option.
         * Also insure that at least one VolCB exists for the disk
         * regardless of /MP.
         * Synch up physical geometry for partitioned removable.       //@V189588
         */
        USHORT i;                                                      /*@V187707*/
        USHORT reserve = ReserveVols[iDisk] ?                          /*@V187707*/
                         ReserveVols[iDisk] : Default_ReserveVols;     /*@V187707*/

        /* Make sure at least one VolCB is reserved */                 //@V187707
        if (reserve == 0)
          reserve = 1;

        if (!(PartitionTables[iDisk].Bad_MBR      ||                   /*@V187707*/
              pPhysVolCB->pUnitCB->Flags & UCF_FLOPPY_FMT_AS_FIXED)    /*@V187707*/
           )
          /* If media isn't formatted as a floppy, and the MBR         //@V187707
           * could be read, then process the extended partitions.
           */
          count+=Setup_Extended_Volumes(pPhysVolCB,1, iDisk);          /*@S170941*/

        if (pPhysVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED)       /*@V187707*/
        {
          /* Reserve VolCBs and synch up physical geometry.
           */
          for (i = count; i < reserve; ++i)                            /*@V187707*/
            Reserve_Next_VolCB(pPhysVolCB);                            /*@V187707*/
          SynchPhysicalGeometry(pPhysVolCB);                           /*@V189588*/
        }
      } /* endif */                                                    /*@S170941*/
   }
   /*------------------------------------------------*/
   /*  Allocate VolCBs for all Extended Partitions   */
   /*------------------------------------------------*/
   if(Linear==FALSE)                                                   /*@S170941*/
   {                                                                   /*@S170941*/
     Setup_Extended_Volumes(pStartScan,Count,StartDiskIndex);          /*@S170941*/
     NumPartitions = NumPartitions - NumFTPartitions;
   } /* endif */                                                       /*@S170941*/

}

/*--------------------------------------------------------------------------
;
;** BuildNextVolCB - Build a VolCB for the next logical drive
;
;   This routine attempts to build a VolCB for a logical
;   fixed disk partition.  It is passed a pointer to the
;   physical VolCB (80H - 86H) of the disk containing the
;   partition and the offset in sectors of that
;   partition sector from the begining of the disk.
;
;   Process_Partition is then called to validate the
;   partition sector and see if there is a DOS partition.
;   If one is found, this VolCB is added to the chain and
;   initialized. The DOS boot sector for the partition
;   is then read in and Process_Boot is called to examine
;   it and build the BPB in the VolCB.
;
;   If there was no valid DOS partition, the VolCB will
;   remain available.
;
;   USHORT Build_Next_VolCB (NPVOLCB pPhysVolCB, ULONG PartitionOffset)
;
;   ENTRY:    pPhysVolCB       - Pointer to VolCB for Physical drive
;             PartitionOffset  - Partition sector offset
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if valid partition)
;
--------------------------------------------------------------------------*/

USHORT Build_Next_VolCB(pPhysVolCB,PartitionOffset)

NPVOLCB pPhysVolCB;
ULONG   PartitionOffset;
{
   ULONG  SectorsInPartition, rba, VolBootRBA, CylinderSize;
   ULONG  SectorsInBootRec;                                          /*@V88662*/

   NPVOLCB pLogVolCB = (NPVOLCB) pNextFreeCB;
   USHORT rc = ERROR;

   if (NumPartitions < MAX_PARTITIONS)
   {
      pLogVolCB->PartitionOffset = PartitionOffset;
      pLogVolCB->MediaBPB.SectorsPerTrack=pPhysVolCB->MediaBPB.SectorsPerTrack;
      pLogVolCB->MediaBPB.NumHeads = pPhysVolCB->MediaBPB.NumHeads;
      pLogVolCB->PartitionType = PARTITION_IFS; // Needed for Is_BPB_Boot() /*@V187707*/

      /* For partitioned removable devices, floppy formatted media   //@V187707
       * is allowed, and made to look like fixed partitioned to the
       * file systems.  If we've been called to process the primary
       * partition, first check for a floppy format.
       */
      if ( pPhysVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED  &&   /*@V187707*/
           PartitionOffset == 0L  &&   // Only check primary         /*@V187707*/
           Is_BPB_Boot (pLogVolCB, (PDOSBOOTREC)ScratchBuffer)       /*@V187707*/
             == NO_ERROR                                             /*@V187707*/
         )
         rc = Process_FloppyAsPartitioned( pLogVolCB,                /*@V187707*/
                 (PULONG) &VolBootRBA, (PULONG) &SectorsInBootRec);  /*@V187707*/

      if (rc == NO_ERROR)                                            /*@V187707*/
         pPhysVolCB->pUnitCB->Flags |= UCF_FLOPPY_FMT_AS_FIXED;      /*@V187707*/
      else
         /* Process as partitioned (it's not a floppy) */            /*@V187707*/
         rc = Process_Partition(pLogVolCB,                           /*@V187707*/
                 (PULONG) &VolBootRBA, (PULONG) &SectorsInBootRec);  /*@V187707*/

      /* If a valid partition is found, initialize the rest of the  */
      /* Logical Volume Control Block.                              */

      if (rc == NO_ERROR)                                            /*@V187707*/
      {
#if 0
         CylinderSize = pLogVolCB->MediaBPB.SectorsPerTrack *
                        pLogVolCB->MediaBPB.NumHeads;

         pLogVolCB->NumLogCylinders = SectorsInPartition  / CylinderSize;

         if ( (SectorsInPartition  % CylinderSize) != 0)
             pLogVolCB->NumLogCylinders++;
#endif

/* defer CylinderSize calculation until media BPB is complete */     /*@V88662*/
         /* Link new VolCB after last logical VolCB  */
         /*  and before physical Drive 80 VolCB      */

         pLogVolCB->pNextVolCB = pVolCB_80;
         pLastLogVolCB->pNextVolCB = pLogVolCB;
         pLastLogVolCB = pLogVolCB;

         /* Copy over applicable fields from the Physical VolCB */

         pLogVolCB->pUnitCB = pPhysVolCB->pUnitCB;
         pLogVolCB->pVolChar = pPhysVolCB->pVolChar;

         if (pLogVolCB->Flags & vf_FTPartition)
           pLogVolCB->LogDriveNum = MAX_DRIVE_LETTERS + NumFTPartitions;
         else
           pLogVolCB->LogDriveNum = NextLogDriveNum++;

         pLogVolCB->PhysDriveNum = pPhysVolCB->PhysDriveNum;
         pLogVolCB->NumPhysCylinders = pPhysVolCB->NumPhysCylinders;

         /* Setup default BPB fields for FORMAT */

         pLogVolCB->MediaBPB.MediaType = MEDIA_FIXED_DISK;
         pLogVolCB->MediaBPB.BytesPerSector = 512;
         pLogVolCB->MediaBPB.ReservedSectors = 1;
         pLogVolCB->MediaBPB.NumFATs = 2;

         pLogVolCB->RecBPB.MediaType = MEDIA_FIXED_DISK;
         pLogVolCB->RecBPB.BytesPerSector = 512;
         pLogVolCB->RecBPB.ReservedSectors = 1;
         pLogVolCB->RecBPB.NumFATs = 2;

         /* Read the DOS boot record into the ScratchBuffer */

         rba = pLogVolCB->PartitionOffset + pLogVolCB->MediaBPB.HiddenSectors;
         rc  = Read_Sector (pPhysVolCB, rba);  /* Read DOS boot sector */
         rc |= Is_BPB_Boot(pLogVolCB,(VOID _far *)&ScratchBuffer);    /*@V88662*//*@V64818*/
         if (!rc)                       /* Is boot sector valid ?      */
         {
            /* copy boot bpb to media bpb */
            BootBPB_To_MediaBPB (pLogVolCB, (DOSBOOTREC FAR *) &ScratchBuffer);
            pLogVolCB->Flags |= vf_GeomFromBPB;                      /*@V191499*/
         }
         else
            pLogVolCB->Flags &= ~vf_GeomFromBPB;                     /*@V191499*/

         /* Call Process_Boot to examine the boot sector and build the BPB */

         if (pLogVolCB->MediaBPB.TotalSectors != 0)
            SectorsInPartition = pLogVolCB->MediaBPB.TotalSectors;
         else
            SectorsInPartition = pLogVolCB->MediaBPB.BigTotalSectors;

         CylinderSize = pLogVolCB->MediaBPB.SectorsPerTrack *        /*@V88662*/
                        pLogVolCB->MediaBPB.NumHeads;                /*@V88662*/
                                                                     /*@V88662*/
         pLogVolCB->NumLogCylinders =                                /*@V89787*/
                      (VolBootRBA + SectorsInBootRec)/CylinderSize;  /*@V89787*/                                               /*@V88662*/
                                                                     /*@V88662*/
         pLogVolCB->BootRecCyl =                                     /*@V89787*/
                      VolBootRBA/CylinderSize;                       /*@V89787*/                                               /*@V88662*/
                                                                     /*@V88662*/
         Process_Boot (pLogVolCB, SectorsInPartition);

         NumPartitions++;
         pLogVolCB++;
         NumVolCBs++;
         (NPVOLCB) pNextFreeCB = pLogVolCB;
         rc = NO_ERROR;
      }
   }
   return(rc);
}

/*--------------------------------------------------------------------------
;                                                                    @V187707
;** Reserve_Next_VolCB - Reserve next logical VolCB
;
;   This routine reserves the next logical VolCB for the given
;   physical drive.  Since actual values cannot be assigned to many of
;   the fields in the VolCB until a physical partition is associated
;   with it, many of the fields are just copied from the physical VolCB.
;
;   The vf_NoAssignedPartition flag is set to indicate that this VolCB
;   has no associated partition.
;
;   USHORT Reserve_Next_VolCB (NPVOLCB pPhysVolCB)
;
;   ENTRY:    pPhysVolCB       - Pointer to VolCB for Physical drive
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if a VolCB
;                                  could be reserved)
;
--------------------------------------------------------------------------*/
                                                                     /*@V187707*/
USHORT Reserve_Next_VolCB(NPVOLCB pPhysVolCB)
{
   NPVOLCB pLogVolCB = (NPVOLCB) pNextFreeCB;

   if (NumPartitions >= MAX_PARTITIONS)
      return ERROR;

   /* Link new VolCB after last logical VolCB
    * and before physical Drive 80 VolCB
    */
   pLogVolCB->pNextVolCB = pVolCB_80;
   pLastLogVolCB->pNextVolCB = pLogVolCB;
   pLastLogVolCB = pLogVolCB;

   /* Copy over applicable fields from the Physical VolCB
    */
   pLogVolCB->pUnitCB = pPhysVolCB->pUnitCB;
   pLogVolCB->pVolChar = pPhysVolCB->pVolChar;
   pLogVolCB->PhysDriveNum = pPhysVolCB->PhysDriveNum;
   pLogVolCB->NumPhysCylinders = pPhysVolCB->NumPhysCylinders;

   /* Copy BPB from physical VolCB.
    * Also, set specific BPB values required in order for FORMAT to work.
    */
   pLogVolCB->RecBPB = pPhysVolCB->RecBPB;
   pLogVolCB->RecBPB.MediaType = MEDIA_FIXED_DISK;
   pLogVolCB->RecBPB.BytesPerSector = 512;
   pLogVolCB->RecBPB.ReservedSectors = 1;
   pLogVolCB->RecBPB.NumFATs = 2;
   pLogVolCB->MediaBPB = pLogVolCB->RecBPB;


   pLogVolCB->PartitionOffset = 0;
   pLogVolCB->PartitionType = PARTITION_IFS;

   pLogVolCB->NumLogCylinders = pLogVolCB->NumPhysCylinders;
   pLogVolCB->BootRecCyl =  0;

   pLogVolCB->Flags = vf_NoAssignedPartition;

   pLogVolCB->LogDriveNum = NextLogDriveNum++;

   NumPartitions++;
   pLogVolCB++;
   NumVolCBs++;
   (NPVOLCB) pNextFreeCB = pLogVolCB;

   return NO_ERROR;
}

/*********************** Start of Specifications **********************
*                                                                     *
* Subroutine Name: Setup_Extended_Volumes                             *
*                                                                     *
* Function: Sets up the VolCB entries for all extended volumes        *
*           found on a given fixed disk.                              *
*                                                                     *
* Entry Point: Setup_Extended_Volumes                                 *
*                                                                     *
* Linkage:  CALL near                                                 *
*                                                                     *
* Input: Physical BDS,                                                *
*        ScratchBuffer containing Master Boot Record                  *
*                                                                     *
* Exit-Normal:                                                        *
*                                                                     *
* Exit-Error:                                                         *
*                                                                     *
************************* End of Specifications ***********************/

USHORT Setup_Extended_Volumes (NPVOLCB pStartScan, USHORT Count, USHORT StartDiskIndex) /*@S170941*/

{
   ULONG   rba, Nextrba, MBRExtRBA;
   MBR     *pMBR;
   NPVOLCB pPhysVolCB;
   USHORT  i, j, found, count=0;
   PARTITIONENTRY *pPartitionEntry;

   pMBR = (MBR *) ScratchBuffer;
   pPhysVolCB = pStartScan;                                                             /*@S170941*/


   /* Search all disks for extended partitions, and setup a VolCB */
   /* for each extended partition found.                          */

   for (i = StartDiskIndex; i < StartDiskIndex+Count; i++, pPhysVolCB++)                /*@S170941*/
   {
//      if (i == 0 && PartitionTables[i].No_PrimPart == 1)
//         continue;

      if (PartitionTables[i].Bad_MBR == 0)
      {
         found = FALSE;
         for  (j = 0; j < 4 && found == FALSE; j++)
         {
           pPartitionEntry = &(PartitionTables[i].PartitionTable[j]);
           if (pPartitionEntry->SysIndicator == PARTITION_EBR)
           {
              rba = pPartitionEntry->RelativeSectors;
              MBRExtRBA = rba;
              found = TRUE;
           }
         }

         /* If the Master Boot Record contained an Extended Partition Type, */
         /* then read in the Extended Boot Record                           */

         while (found == TRUE)
         {
            found = FALSE;
            if (Read_Sector(pPhysVolCB, rba) == NO_ERROR)
            {
               for  (j = 0; j < 4 && found == FALSE; j++)
               {
                  pPartitionEntry = &(pMBR->PartitionTable[j]);
                  if (pPartitionEntry->SysIndicator == PARTITION_EBR)
                  {
                     Nextrba = pPartitionEntry->RelativeSectors + MBRExtRBA;
                     found = TRUE;
                  }
               }
               if(Build_Next_VolCB(pPhysVolCB, rba)==NO_ERROR)
                 count++;                                                               /*@S170941*/
               rba = Nextrba;
            }
         }
      }
   }
   return count;
}

/*---------------------------------------------------------------
;
;** Read_Sector - performs disk reads during initialization
;
;   Reads a sector from a fixed disk into ScratchBuffer.
;   This routine sets up a hard coded read request packet
;   and calls the Adapter Driver to perform the read.
;
;   USHORT Read_Sector (NPVOLCB pPhysVolCB, ULONG rba)
;
;   ENTRY:    pPhysVolCB       - Physical VolCB of disk to read
;             rba              - RBA of sector to read
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if successful)
;
;   EFFECTS:  Reads a sector into global variable ScratchBuffer.
;
;   NOTES:    This routine is DISCARDED after init time.
;--------------------------------------------------------------*/

USHORT  Read_Sector(pPhysVolCB, rba)

NPVOLCB  pPhysVolCB;
ULONG    rba;

{
   PRP_RWV  pRP;
   NPIORB   pIORB;
   NPUNITCB pUnitCB;
   USHORT   rc;


   /* Set up the request packet for the read */

   pRP = &InitTimeRP;
   pRP->rph.Unit = pPhysVolCB->PhysDriveNum;
   pRP->rph.Cmd = CMDINPUT;
   pRP->rph.Status = 0;
   pRP->MediaDescr = MEDIA_FIXED_DISK;
   pRP->XferAddr = ppScratchBuffer;      /* Point to scratch buffer */
   pRP->NumSectors = 1;                  /* Read 1 sector */
   pRP->rba = rba;                       /* Store rba */
   pRP->sfn = 512;                       /* Use sfn field for SectorSize */

   pUnitCB = pPhysVolCB->pUnitCB;

   pIORB = (NPIORB) InitTimeIORB;
   f_ZeroCB((PBYTE)pIORB, MAX_IORB_SIZE);

   SetupIORB(pUnitCB, (PBYTE) pRP, pIORB);

   (pUnitCB->AdapterDriverEP) ((PVOID) pIORB);

   DISABLE;
   while ( !(pRP->rph.Status & STDON) )    /* Loop until I/O done   */
   {
      DevHelp_ProcBlock ((ULONG)pRP, -1L, 1);  /* Block: No timeout,non-interruptible*/
      DISABLE;                          /* Block does an enable  */
   }
   ENABLE;

   if (pRP->rph.Status & STERR)          /* Check for error */
      rc = ERROR;
   else
      rc = NO_ERROR;

   return(rc);
}

/*--------------------------------------------------------------------------
;
;** InitDCS_VCS
;
;   Initialize the DCS and VCS control blocks
;
;   This function will initialize the statically allocated
;   DriverCapabilities Structure (DCS). It will also dynamically
;   allocate one Volume Characteristics Structure (VCS) for each unit
;   and initialize it.
;
;   VOID InitDCS_VCS ()
;
;   ENTRY:
;
;   RETURN:   VOID
;---------------------------------------------------------------------------*/

VOID InitDCS_VCS()

{
   VolChars *pVCS;
   NPUNITCB pUnitCB;
   USHORT   i;
   NPVOLCB  pVolCB;

   /* Initialize the Driver Capabilites Structure */

   DriverCapabilities.VerMajor = 1;
   DriverCapabilities.VerMinor = 0;
   DriverCapabilities.Capabilities = GDC_DD_Mirror   | GDC_DD_Duplex |
                                     GDC_DD_No_Block | GDC_DD_16M;
   DriverCapabilities.Strategy2   = (PVOID) DMStrat2;
   DriverCapabilities.SetFSDInfo  = (PVOID) DD_SetFSDInfo;
   DriverCapabilities.ChgPriority = (PVOID) DD_ChgPriority_asm;
   DriverCapabilities.SetRestPos  = 0;
   DriverCapabilities.GetBoundary = 0;


   /* Allocate and initialize a VCS for each volume */

   pVCS = (VolChars *) pNextFreeCB;
   pVolCB = pVolCB_DriveA;

   for (i = 0; i < NumVolCBs; i++)
   {
      pVolCB->pVolChar = pVCS;

      pVCS->VolDescriptor = 0;
      pVCS->AvgSeekTime = -1;
      pVCS->AvgLatency =  -1;
      pVCS->TrackMinBlocks = pVolCB->RecBPB.SectorsPerTrack;
      pVCS->TrackMaxBlocks = pVolCB->RecBPB.SectorsPerTrack;
      pVCS->HeadsPerCylinder = pVolCB->RecBPB.NumHeads;
      pVCS->VolCylinderCount = pVolCB->NumLogCylinders;

      if (pVolCB->pUnitCB != 0)
      {
         pUnitCB = pVolCB->pUnitCB;

         pVCS->VolMedianBlock = pUnitCB->LastRBA / 2;
         pVCS->MaxSGList = pUnitCB->MaxHWSGList;
         if (pUnitCB->MaxHWSGList == 0)
            pVCS->MaxSGList = -1;

         if (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
            pVCS->VolDescriptor |= VC_REMOVABLE_MEDIA;

         if (pUnitCB->UnitInfo.UnitFlags & UF_PREFETCH)
            pVCS->VolDescriptor |= VC_PREFETCH;

         if (pUnitCB->Flags & UCF_HW_SCATGAT)
            pVCS->VolDescriptor |= VC_SCB;

         if (pUnitCB->UnitInfo.UnitType == UIB_TYPE_CDROM)
            pVCS->VolDescriptor |= VC_READ_ONLY;
      }
      pVCS++;
      pVolCB = pVolCB->pNextVolCB;
   }
   pNextFreeCB = (NPBYTE) pVCS;
}


/* Dummy notification callout for ADDs during init processing */

VOID FAR InitPost(pIORB)

PIORB pIORB;
{

}

void upcase(PSZ pStr)
{
  for(;*pStr;pStr++)
    {
    if(*pStr>='a' && *pStr<='z')
      {
      *pStr&=0xdf;
      } /* endif */
    } /* endwhile */
}

/*--------------------------------------------------------------------------
;                                                                    @V187707
;** ScanInt - Scan an integer value from a given string
;
;   Note: Leading whitespace characters are NOT skipped.
;   It is assumed that the given string points to first digit in the integer.
;
;   USHORT ScanInt (PSZ str, USHORT *pval)
;
;   ENTRY:    str     - Pointer to start of integer in string
;             pval    - Pointer to return integer value
;
;   RETURN:   USHORT  - Count of number of characters scanned
;
;---------------------------------------------------------------------------*/

USHORT ScanInt(PSZ str, PUSHORT pval)                                /*@V187707*/
{
   USHORT count = 0;
   USHORT val = 0;

   while (*str >= '0' && *str <= '9') {
      val = val * 10 + (*str - '0');
      ++count;
      ++str;
   }
   *pval = val;
   return count;
}

/*--------------------------------------------------------------------------
;                                                                    @V187707
;  ScanWhiteSpace ( PSZ str )
;
;  Update given parameter to point past leading white space characters.
;
;*/
#define ScanWhiteSpace(str)  \
   while (*str == ' ' || *str == '\t')  ++str

/*--------------------------------------------------------------------------
;                                                                    @V187707
;  ScanSeperator ( PSZ str )
;
;  Update given parameter to point past leading seperator characters.
;
;*/
#define ScanSeperator(str)  \
   while (*str == ' ' || *str == '\t' || *str == ',')  ++str

/*--------------------------------------------------------------------------
;                                                                    @V187707
;** ScanPartitionOption - Scan a single partition option for /MP
;
;   The syntax of the partition option scanned is:
;      [ , ] ( [ * | <disk> ] , <count> )
;   where <disk> and <count> are positive integers, whose value is
;   returned through the 'pdisk' and 'pcount' parameters.
;   If a '*' is scanned in place of <disk>, 0 is returned through 'pdisk'.
;
;   USHORT ScanPartitionOption (PSZ str, PUSHORT pdisk, PUSHORT pcount)
;
;   ENTRY:    str     - Pointer to start of string to scan
;             pdisk   - Pointer to return disk integer value
;             pcount  - Pointer to return count integer value
;
;   RETURN:   USHORT  - Count of number of characters scanned
;
;---------------------------------------------------------------------------*/

USHORT ScanPartitionOption(PSZ str, PUSHORT pdisk, PUSHORT pcount)
                                                             /*@V187707*/
{
   PSZ startStr = str;
   USHORT sz;

   ScanSeperator(str);
   if (*str++ != '(')   return 0;

   ScanWhiteSpace(str);

   if (*str == '*')
   {
      *pdisk = 0;
      sz = 1;
   }
   else
   {
      sz = ScanInt(str, pdisk);
      if (sz == 0 || *pdisk == 0)
         return 0;
   }
   str += sz;

   ScanSeperator(str);
   sz = ScanInt(str, pcount);
   if (sz == 0)  return 0;
   str += sz;
   ScanWhiteSpace(str);
   if (*str++ != ')')  return 0;

   return (str - startStr);
}

/*--------------------------------------------------------------------------
;
;** GetInitParms - Get init parms from BASEDEV command line
;
;   VOID GetInitParms (PRPINITIN pRP);
;
;   ENTRY:    pRP              - Pointer to init request packet
;
;   RETURN:   VOID
;
;   EFFECTS:  Turns on Queueing flags in global DDFlags.
;
;---------------------------------------------------------------------------*/
VOID GetInitParms (pRP)

PRPINITIN pRP;

{
   PSZ    pCmdString;
   USHORT i;

   pCmdString = pRP->InitArgs;
   OFFSETOF(pCmdString) = ((PDDD_PARM_LIST)pRP->InitArgs)->cmd_line_args;

   DefaultSortMethod  = SORT_METHOD_ELEVATOR;                        /*@V74404*/
   DefaultQueueMethod = QUEUE_METHOD_PRIORITY;                       /*@V74404*/
   upcase(pCmdString);
   for (i = 0; *pCmdString != 0 && i < 40; i++, pCmdString++)
   {
      if (*pCmdString == '/')
      {
         if ( ( *(pCmdString+1) == 'Q' || *(pCmdString+1) == 'q' ) &&
              ( *(pCmdString+2) == 'F' || *(pCmdString+2) == 'f' ) )
         {
            if ( *(pCmdString + 3) == ':' )
            {
               switch ( *(pCmdString + 4) )
               {
                  case '1':
                      DefaultSortMethod  = SORT_METHOD_FIFO;         /*@V74404*/
                      break;

                  case '2':
                      DefaultQueueMethod = QUEUE_METHOD_NOPRIORITY;  /*@V74404*/
                      break;

                  case '3':
                      DefaultSortMethod  = SORT_METHOD_FIFO;         /*@V74404*/
                      DefaultQueueMethod = QUEUE_METHOD_NOPRIORITY;  /*@V74404*/
                      break;
               }
            }
         }
         else if ( ( *(pCmdString+1) == 'T' || *(pCmdString+1) == 't' ) &&
                   ( *(pCmdString+2) == 'R' || *(pCmdString+2) == 'r' ) )
            {
               TraceFlags |= TF_INTERNAL;
            }
         else if ( ( *(pCmdString+1) == 'L' || *(pCmdString+1) == 'l' ) &&     /*@V184996*/
                   ( *(pCmdString+2) == 'F' || *(pCmdString+2) == 'f' ) )      /*@V184996*/
            {
               TreatAsFixed= FALSE;                                         /*@S170941*/
            }
         else if ( ( *(pCmdString+1) == 'O' || *(pCmdString+1) == 'o' ) &&  /*@S170941*/
                   ( *(pCmdString+2) == 'F' || *(pCmdString+2) == 'f' ) )   /*@S170941*/
            {                                                               /*@S170941*/
               OpticalAsFixed= TRUE;                                        /*@S170941*/
               TreatAsFixed= TRUE;                                          /*@S170941*/
            }                                                               /*@S170941*/
         else if ( ( *(pCmdString+1) == 'M' || *(pCmdString+1) == 'm' ) &&  /*@V187707*/
                   ( *(pCmdString+2) == 'P' || *(pCmdString+2) == 'p' ) )   /*@V187707*/
            {                                                               /*@V187707*/
               /* Scan /MP option                                           //@V187707
                *      /MP:<PartitionOption> [,<PartitionOption>...]        //@V187707
                */                                                          //@V187707
               if ( *(pCmdString + 3) == ':' )                              /*@V187707*/
               {                                                            /*@V187707*/
                  PSZ str = pCmdString + 4;                                 /*@V187707*/
                  USHORT sz;                                                /*@V187707*/
                  USHORT disk, count;                                       /*@V187707*/

                  /* Process list of Partition Options */                   //@V187707
                  do                                                        /*@V187707*/
                  {                                                         /*@V187707*/
                     sz = ScanPartitionOption(str, (PUSHORT)&disk, (PUSHORT)&count); /*@V187707*/
                     str += sz;                                             /*@V187707*/
                     if (sz > 0  &&  disk <= MAX_FIXED_DISKS)               /*@V187707*/
                     {                                                      /*@V187707*/
                        if (count > MAX_FIXED_DISKS)                        /*@V187707*/
                          count = MAX_FIXED_DISKS;                          /*@V187707*/
                        if (disk)                                           /*@V187707*/
                          ReserveVols[disk-1] = count;                      /*@V187707*/
                        else                                                /*@V187707*/
                          Default_ReserveVols = count;                      /*@V187707*/
                     }                                                      /*@V187707*/
                  }                                                         /*@V187707*/
                  while (sz > 0);                                           /*@V187707*/

                  /* Update pCmdString to point past /MP option.
                   */
                  pCmdString = str;                                         /*@V187707*/
               }                                                            /*@V187707*/
            }                                                               /*@V187707*/
      }
   }
}

/*--------------------------------------------------------------------------
;
;** Init_Trace -  Initialize RAS, DEKKO, PERFVIEW and Internal tracing
;
;   VOID Init_Trace ()
;
;   ENTRY:
;
;   RETURN:   VOID
;---------------------------------------------------------------------------*/
VOID Init_Trace()
{

   PPVOID   ppSysInfoSeg;
   NPUNITCB pUnitCB;
   USHORT   i;

   /* Get pointer to RAS Major Event Code Table */

   DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &ppSysInfoSeg);

   SELECTOROF(pSysInfoSeg) = (USHORT) *ppSysInfoSeg;
   OFFSETOF(pSysInfoSeg) = 0;

   pSIS_mec_table = ((PInfoSegGDT)pSysInfoSeg)->SIS_mec_table;


   /* Register PerfView Data and Text Blocks for each Non-Removable */
   /* physical unit.                                                */

   PerfViewInstalled = 1;                                            /*@V81576*/

   pUnitCB = UnitCB_Head;
   for (i = 0; i < NumUnitCBs; i++, pUnitCB++)
   {
      if ( !(pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) )
      {
         pUnitCB->PerfViewDB.pfdbh.dbh_ulTotLen = sizeof(PVDB);
         pUnitCB->PerfViewDB.pfdbh.dbh_flFlags = RPC_FL_16BIT | RPC_FL_DD;

         PerfViewTB.tbh_bidID.bid_usInstance = 0;
         PerfViewTB.tbh_bidID.bid_usGroup = 0;

         if ( DevHelp_RegisterPerfCtrs( (NPBYTE)&pUnitCB->PerfViewDB,        /*@V81576*/
                                        (NPBYTE)&PerfViewTB, RPC_FL_16BIT ) )/*@V81576*/
         {                                                            /*@V81576*/
           PerfViewInstalled = 0;                                     /*@V81576*/
           break;                                                     /*@V81576*/
         }                                                            /*@V81576*/

         if (GroupName[6] == '9')
         {
            GroupName[6]='0';
            GroupName[5]++;
         }
         else
            GroupName[6]++;
      }
   }

   /* If internal tracing enabled, then allocate trace buffer  */
   /* Note: Always will be enough since init data is > 1 K.    */

   if ( IsTraceNeeded() )                                            /*@V81576*/
   {                                                                 /*@V81576*/
      pDMTraceBuf  = pNextFreeCB;
      pNextFreeCB += TRACEBUF_SIZE;
      pDMTraceHead = pDMTraceBuf;
      pDMTraceEnd  =  pDMTraceBuf + TRACEBUF_SIZE;
   }                                                                 /*@V81576*/
   else                                                              /*@V81576*/
   {                                                                 /*@V81576*/
     DDFlags |= DDF_DISCARD_TRACE;                                   /*@V81576*/
   }                                                                 /*@V81576*/
}


VOID NEAR InitRMInfo()
{
  NPVOLCB       pVolCB;
  NPUNITCB      pUnitCB;

  HDEVICE       hRMDevice;
  HLDEV         hRMLDev;
  HSYSNAME      hRMSysName;

  LDEVSTRUCT    RMLDevStr;
  SYSNAMESTRUCT RMSysNameStr;

  ADJUNCT       RMAdjunct;

  PSZ           s;

  USHORT        cRemove = 0;
  USHORT        cFixed  = 0;
  USHORT        cDskt   = 0;

  USHORT        UnitFlags;
  USHORT        rc;

  memset( (PSZ) &RMLDevStr, 0, sizeof(LDEVSTRUCT) );

  RMLDevStr.LDevClass        = LDEV_CLASS_DASD;
  RMLDevStr.pAdjunctList     = &RMAdjunct;
  RMAdjunct.AdjType          = ADJ_DEVICE_NUMBER;
  RMAdjunct.AdjLength        = sizeof(ADJUNCT);
  RMAdjunct.pNextAdj         = NULL;

  pUnitCB = UnitCB_Head;
  while ( pUnitCB )
  {
    UnitFlags = pUnitCB->UnitInfo.UnitFlags;

    if ( UnitFlags & (UF_A_DRIVE | UF_B_DRIVE) )
    {
      RMLDevStr.LDevDescriptName = RMDskt_Txt;
      RMAdjunct.Device_Number = cDskt;
    }
    else if ( UnitFlags & UF_REMOVABLE )
    {
      RMLDevStr.LDevDescriptName = RMRemovable_Txt;
      RMAdjunct.Device_Number = cRemove++;
    }
    else
    {
      RMLDevStr.LDevDescriptName = RMFixed_Txt;
      RMAdjunct.Device_Number = cFixed++;
    }

    rc = RMADDToHDEVICE( (PHDEVICE) &hRMDevice,
                         (USHORT)   pUnitCB->ADDHandle,
                         (USHORT)   pUnitCB->UnitInfo.UnitHandle );

    if ( !rc && (hRMDevice != -1L) )
    {
      if ( !(rc = RMCreateLDev( (HDRIVER)     hRMOS2DASD,
                                (PHLDEV)      &hRMLDev,
                                (HDEVICE)     hRMDevice,
                                (PLDEVSTRUCT) &RMLDevStr  )) )
      {
        if ( hRMLDev != -1L )
        {
          pUnitCB->hRMLDev = hRMLDev;
        }
      }
    }

    pUnitCB = pUnitCB->pNextUnitCB;
  }

  pVolCB = VolCB_Head;

  memset( (PSZ) &RMSysNameStr, 0, sizeof(SYSNAMESTRUCT) );

  RMSysNameStr.SysDescriptName = RMSysName_Txt;
  RMSysNameStr.pAdjunctList    = &RMAdjunct;

  RMAdjunct.AdjType            = ADJ_DASD_VOL;
  RMAdjunct.pNextAdj           = 0;
  RMAdjunct.AdjLength          = sizeof(ADJUNCT);

  while ( pVolCB )
  {
    pUnitCB = pVolCB->pUnitCB;

    if (pVolCB->LogDriveNum < 26 )
    {
      RMSysName_Txt[0] = pVolCB->LogDriveNum + 'A';

      if ( pVolCB->RecBPB.TotalSectors )
      {
        RMAdjunct.Dasd_Vol.VolSize = pVolCB->RecBPB.TotalSectors;
      }
      else
      {
        RMAdjunct.Dasd_Vol.VolSize = pVolCB->RecBPB.BigTotalSectors;
      }

      RMAdjunct.Dasd_Vol.VolIFSType = pVolCB->PartitionType;

      if ( hRMLDev = pUnitCB->hRMLDev )
      {
        if ( !(rc = RMCreateSysName( (HDRIVER)        hRMOS2DASD,
                                     (PHSYSNAME)      &hRMSysName,
                                     (HDEVICE)        pUnitCB->hRMLDev,
                                     (PSYSNAMESTRUCT) &RMSysNameStr  )) )
        {
          if ( hRMSysName != -1L )
          {
            pVolCB->hRMSysName = hRMSysName;
          }
        }
      }
    }

    pVolCB = pVolCB->pNextVolCB;
  }
}


VOID NEAR memset( PSZ d, USHORT Value, USHORT n )
{
  USHORT        i;

  for ( i=0; i < n; i++ )
  {
    *d++ = (UCHAR) Value;
  }
}

/* @V111573 Begin */
/*--------------------------------------------------------------------------
;
;** InitLogData -  Initialize specific fields of Error Logging Structures
;
;   VOID InitLogData (VOID)
;
;---------------------------------------------------------------------------*/

VOID NEAR InitLogData(VOID)
{

  GenAlert_URErr.LogHeader.PacketLen = sizeof(GenAlert_URErr) - 4;
  GenAlert_URErr.LogHeader.PacketId = SNAGENALERT;

  GenAlert_URErr.GenAlertSubvector.GAS_Type = GAS_PERM_LOSS;
  GenAlert_URErr.GenAlertSubvector.GAS_Desc = GAS_ADC_DSKFAIL;
  GenAlert_URErr.GenAlertSubvector.GAS_AlertId = HARD_CRC;

  GenAlert_URErr.GAS_Probable_Cause.GAS_PCS_Desc = GAS_PCD_DASD;

  GenAlert_URErr.GAS_Failure_Cause.GAS_FCS_CauseCd = GAS_FCC_DISKDRV;
  GenAlert_URErr.GAS_Failure_Cause.GAS_FCS_RecAct = GAS_RAC_ONLINEPD;



  GenAlert_RErr.LogHeader.PacketLen = sizeof(GenAlert_RErr) - 4;
  GenAlert_RErr.LogHeader.PacketId = SNAGENALERT;

  GenAlert_RErr.GenAlertSubvector.GAS_Type = GAS_TEMP_LOSS;
  GenAlert_RErr.GenAlertSubvector.GAS_Desc = GAS_ADC_DSKOPER;
  GenAlert_RErr.GenAlertSubvector.GAS_AlertId = SOFT_CRC;

  GenAlert_RErr.GAS_Probable_Cause.GAS_PCS_Desc = GAS_PCD_DASD;

  GenAlert_RErr.GAS_Failure_Cause.GAS_FCS_CauseCd = GAS_FCC_DISKDRV;
  GenAlert_RErr.GAS_Failure_Cause.GAS_FCS_RecAct = GAS_RAC_REF_GUIDE;

}
/* @V111573 End */

