/*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 = "src/dev/dasd/os2dasd/dmcsubr.c, dsdm, basedd, initial 93/03/20";*/
#define SCCSID  "src/dev/dasd/os2dasd/dmcsubr.c, dsdm, basedd, initial 93/03/20"

/**************************************************************************
 *
 * SOURCE FILE NAME = DMCSUBR.C
 *
 * DESCRIPTIVE NAME = OS2DASD.DMD - OS/2 DASD Device Manager
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Miscellaneous C subroutines/utility fcns.
 *
 *
 *
*/
#include "dmh.h"

/*------------------------------------------------------------------------
;
;** Get_VolCB_Addr - Return a pointer to a VolCB for the specified Drive
;
;   USHORT NEAR Get_VolCB_Addr   (USHORT DriveNum, NPVOLCB *pVolCB)
;
;   USHORT FAR  f_Get_VolCB_Addr (USHORT DriveNum, NPVOLCB *pVolCB)
;
;   ENTRY:    DriveNum         - Drive Number
;             pVolCB           - returned pointer to VolCB
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if VolCB found)
;
;   EFFECTS:
;
;   NOTES:
------------------------------------------------------------------------*/
USHORT FAR f_Get_VolCB_Addr (DriveNum, pVolCB)

USHORT  DriveNum;
NPVOLCB FAR *pVolCB;
{
   return(Get_VolCB_Addr (DriveNum, (NPVOLCB FAR *) pVolCB));

}

USHORT Get_VolCB_Addr (DriveNum, pVolCB)

USHORT  DriveNum;
NPVOLCB FAR *pVolCB;
{
   NPVOLCB pVolCBx;

   if ( !(DDFlags & DDF_NO_MEDIA) )
   {
      /* Start d159983 */

      if (*pVolCB = DriveToVolCB[DriveNum])
      {
        return(NO_ERROR);
      }

      /* End   d159983 */

// d159983                                          /* 154306 hint lookup optimization */
// d159983     if ((VolCB_Hint != NULL) && (VolCB_Hint->LogDriveNum == DriveNum)) {
// d159983        *pVolCB = VolCB_Hint;
// d159983        return(NO_ERROR);
// d159983     }

      pVolCBx = VolCB_Head;

                                              /* 154306 optimized lookup loop */
      while (pVolCBx != NULL)
      {
         if (pVolCBx->LogDriveNum == DriveNum) {
            *pVolCB = pVolCBx;
// d159983            VolCB_Hint = pVolCBx;
            return(NO_ERROR);
         }
         pVolCBx = pVolCBx->pNextVolCB;
      }
   }

   return(ERROR);
}


/*------------------------------------------------------------------------
;                                                                   @V187707
;** FirstLogicalVolCB - Return pointer to first logical VolCB associated
;                       with a given physical VolCB
;
;   NPVOLCB FirstLogicalVolCB   (NPVOLCB pPhysVolCB)
;
;   ENTRY:    pPhysVolCB       - pointer to physical VolCB
;
;   RETURN:   NPVOLCB          - pointer to first logical VolCB
;
;   EFFECTS:
;
;   NOTES:
;      NextLogicalVolCB (NPVOLCB pLogVolCB)
;      is defined as a macro in dmdefs.h
------------------------------------------------------------------------*/

NPVOLCB FirstLogicalVolCB(NPVOLCB pPhysVolCB)                        /*@V187707*/
{
   NPVOLCB  pVolCB;
   NPUNITCB pPhysUnitCB = pPhysVolCB->pUnitCB;
   USHORT   i;

   pVolCB = VolCB_Head;

   //HCT Walk the whole VolCB chain
   while (pVolCB) {
      if (pVolCB->pUnitCB == pPhysUnitCB)
         return pVolCB;                      // found it

      pVolCB = pVolCB->pNextVolCB;
   }
   return NULL;         // not found!! error ???

}


/*---------------------------------------------------------------
;
;** ReadSecInScratch_RBA - Read sector into scratch buffer
;
;   Reads a sector into the ScratchBuffer.
;
;   USHORT ReadSecInScratch_RBA (NPVOLCB pVolCB, ULONG rba, USHORT type)
;
;   ENTRY:    pVolCB           - Pointer to VolCB
;             rba              - RBA of sector to read
;             type             - Type of request (0= add in hidden sectors,
;                                                 1= dont add in hidden sectors)
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if successful)
;
;   EFFECTS:  Reads a sector into global variable ScratchBuffer.
;
;   NOTES:
;
;   SEGMENT:  StaticCode
;--------------------------------------------------------------*/
USHORT FAR f_ReadSecInScratch_RBA (pVolCB, rba, type)

NPVOLCB  pVolCB;
ULONG    rba;
USHORT   type;
{
   return (ReadSecInScratch_RBA (pVolCB, rba, type));
}

USHORT ReadSecInScratch_RBA (pVolCB, rba, type)

NPVOLCB  pVolCB;
ULONG    rba;
USHORT   type;
{
   PRP_INTERNAL  pRP;                                                /*@V59914*/
   USHORT rc;

   DevHelp_AllocReqPacket (0, (PBYTE FAR *)&pRP); /* Allocate a request packet */

   pRP->rph.Cmd = CMDINPUT;
   pRP->rph.Flags = RPF_Internal;       /* Internal read request */
   pRP->rph.Unit = pVolCB->LogDriveNum; /* Copy unit number */
   pRP->XferAddr = ppScratchBuffer;     /* Set xfer addr of ScratchBuffer */
   pRP->NumSectors = 1;                 /* read one sector */
   pRP->rba = rba;                      /* Setup rba */
   pRP->SectorSize = 0;                 /* Setup Sector Size */      /*@V59914*/

   pVolCB->Flags |= vf_ForceRdWrt;      /* force next read to succeed */

   pRP->rba += pVolCB->PartitionOffset; /* Add in partition offset    */

   if (type == 0)
      pRP->rba += pVolCB->MediaBPB.HiddenSectors; /* Add in Hidden sectors */

   DiskIO_Wait ((PBYTE)pRP, pVolCB);    /* Issue the read request */

   rc = pRP->rph.Status;                /* Get return status */

   DevHelp_FreeReqPacket ((PBYTE)pRP);         /* Free the request packet */

   if (rc & STERR)                      /* Check for error */
      return(ERROR);
   else
      return(NO_ERROR);
}

/*---------------------------------------------------------------
;
;** ReadSecInScratch_CHS - Read sector into scratch buffer
;
;   Reads a sector into the ScratchBuffer.
;
;   USHORT ReadSecInScratch_CHS (NPVOLCB pVolCB, USHORT Cylinder,
;                                UCHAR Head, UCHAR Sector)
;
;   ENTRY:    pVolCB           - pointer to VolCB
;             Cylinder         - Cylinder
;             Head             - Head
;             Sector           - Sector
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if successful)
;
;   EFFECTS:  Reads a sector into global variable ScratchBuffer.
;
;   NOTES:
;
;   SEGMENT:  SwapCode
;--------------------------------------------------------------*/
USHORT FAR f_ReadSecInScratch_CHS (pVolCB, Cylinder, Head, Sector)

NPVOLCB  pVolCB;
USHORT   Cylinder;
UCHAR    Head, Sector;

{
  return (ReadSecInScratch_CHS (pVolCB, Cylinder, Head, Sector));
}

USHORT ReadSecInScratch_CHS (pVolCB, Cylinder, Head, Sector)

NPVOLCB  pVolCB;
USHORT   Cylinder;
UCHAR    Head, Sector;

{
   return (ReadSecInScratch_RBA (pVolCB,
                                 f_CHS_to_RBA (pVolCB, Cylinder, Head, Sector),
                                 1));
}




/*------------------------------------------------------------------------
;
;** w_MediaCheck - Worker routine for Media Check
;
;   Checks to see if the media in the drive has changed.
;
;   USHORT w_MediaCheck   (UCHAR Unit, NPVOLCB pVolCB)
;
;   ENTRY:    Unit             - Logical Unit Number
;             pVolCB           - Pointer to VolCB
;
;   RETURN:   USHORT           - Media Change status as follows:
;                                   -1 = Media has been changed
;                                    0 = Unsure if media has been changed
;                                    1 = Media unchanged
;
------------------------------------------------------------------------*/
USHORT w_MediaCheck (Unit, pVolCB)

UCHAR    Unit;
NPVOLCB  pVolCB;

{
   USHORT  ChangeState;
   NPVOLCB pVolCBx;

   /* Not changed if fixed disk */

   if ( !(pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) &&
        !(pVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED) )
      return (MEDIA_UNCHANGED);

   if(!(pVolCB->pUnitCB->Flags & UCF_REMOVABLE_AS_FIXED))
   {

     if ( (CheckPseudoChange(Unit, pVolCB) != 0) ||
          (pVolCB->Flags & (vf_ChangedByFormat | vf_Changed)) )

        ChangeState = MEDIA_CHANGED;

     else
     {
        ChangeState = CheckChangeSignal (Unit, pVolCB);

        /* See if changeline support is there for this unit */

        if ( !(pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_CHANGELINE) )
        {
           if (ChangeState != MEDIA_UNCHANGED)
              ChangeState = MEDIA_UNSURE_CHANGED;
        }
        else

        /* Changeline is supported by the drive and the ChangeLine is not
           active.  This does not mean that it was not triggered!!!!
           It could be the case that it was high, but an access to the second
           floppy drive turned it off (this was a problem reported during
           DOS 3.20). We therefore check to see if this unit was the last
           one accessed if we are on a system with a single floppy drive.
           If it was, then we assume the medium has not changed.
        */
        {
           if ( (ChangeState == MEDIA_UNCHANGED) && (NumRemovableDisks == 1) &&
                (pVolCB->LogDriveNum <= 1) && (XActPDrv != pVolCB->LogDriveNum) )

                ChangeState = MEDIA_UNSURE_CHANGED;
        }
     }
   } /* endif */
   else
   {
     // uncertain media flag set by eject
     if(pVolCB->Flags & vf_UncertainMedia)
       ChangeState = MEDIA_UNSURE_CHANGED;
     else
       ChangeState = MEDIA_UNCHANGED;

   } /* endelse */
      /*
         If the medium may have changed or has changed we note this in the
         VolCB for later use. We *MUST* flag these changes in ALL the
         logical drives that are mapped onto this physical drive.
         We also inform the ROM that the medium may have changed.
         This must be done so that it retries I/O on the medium with
         different data rates, and does not assume a certain rate for
         the particular drive. This is only done for physical drives
         0 and 1 since the ROM only has data areas for these drives.
      */

   if (ChangeState == MEDIA_CHANGED)
      pVolCB->Flags &= ~(vf_ChangedByFormat | vf_Changed);


   if (ChangeState != MEDIA_UNCHANGED)
   {
      for (pVolCBx = VolCB_Head; pVolCBx != NULL; pVolCBx = pVolCBx->pNextVolCB)
      {
         if (pVolCBx->PhysDriveNum == pVolCB->PhysDriveNum)
            pVolCBx->Flags |= vf_UncertainMedia;
      }
   }

   return(ChangeState);
}
/*------------------------------------------------------------------------
;
;***    CheckPseudoChange - check for floppy pseudo drive change
;
;       The DOS/BIOS conspire so that on single-floppy-drive systems there
;       appear to be two floppy drives, "A" and "B".  The Bios keeps track
;       of which of these "pseudo drives" is currently active and if it
;       sees a request to the other drive causes the user to be prompted
;       to "insert drive ?? disk".
;
;       CheckPseudoChange is called when we're doing floppy I/O on a single
;       drive system.  It takes one of two actions:
;
;       1) the request is for the pseudo-drive thats active
;               - return no change required
;
;       2) the request is for the other pseodo-drive:
;               - Return disk change error to dos and let it map the
;                 the logical drive.
;
;   USHORT CheckPseudoChange (USHORT LogDriveNum, NPVOLCB pVolCB)
;
;   ENTRY:    LogDriveNum      - Logical Drive Number
;             pVolCB           - Pointer to VolCB
;
;   RETURN:   USHORT           - Pseudo drive status:
;                                   -1 = Pseudo drive must be changed
;                                    0 = Pseudo drive unchanged
;
------------------------------------------------------------------------*/
USHORT FAR f_CheckPseudoChange (LogDriveNum, pVolCB)

USHORT  LogDriveNum;
NPVOLCB pVolCB;

{
   return(CheckPseudoChange (LogDriveNum, pVolCB));
}

USHORT CheckPseudoChange (LogDriveNum, pVolCB)

USHORT  LogDriveNum;
NPVOLCB pVolCB;

{
   USHORT ChangeState;

   /* If not one of several logical drives or this unit not  */
   /* mapped onto the physical drive then return unchanged   */

   if ( !(pVolCB->Flags & vf_AmMult) || (pVolCB->Flags & vf_OwnPhysical) )
      ChangeState = 0;

   else if (NumRemovableDisks != 1)
      ChangeState = -1;

/*
 *  If we are trying to switch back to either the A: or the B: drive
 *  and if neither of the drives A: or B: owns the physical device then
 *  the device must be allocated to a EXTDSKDD.SYS device and we
 *  cannot trust the ROMData to tell us which device owns the drive
 *  because neither of the ActPDrv(s) own it!
 */

   else if ( (LogDriveNum == 0 || LogDriveNum == 1) &&         /* If A: or B: */
             !(VolCB_Head->Flags & vf_OwnPhysical) &&          /* A: owns ?   */
             !(VolCB_Head->pNextVolCB->Flags & vf_OwnPhysical) )/* B: owns ?   */

        ChangeState = -1;


   else if (XActPDrv == LogDriveNum)
   {
       Update_Owner (pVolCB);
       ChangeState = 0;
   }
   else
       ChangeState = -1;

  return (ChangeState);
}
/*------------------------------------------------------------------------
;
;** Update_Owner - Update the owner of the logical drive
;
;   Update_Owner maps the current drive onto the physical drive by
;   adjusting the vf_OwnPhysical flag bits in the approporiate VolCBs.
;   It also adjusts the ROMs data area.
;
;   VOID Update_Owner (NPVOLCB pVolCB)
;
;   ENTRY:    pVolCB           - Pointer to VolCB
;
;   RETURN:
;
------------------------------------------------------------------------*/
VOID Update_Owner (pVolCB)

NPVOLCB pVolCB;

{
   NPVOLCB pVolCBx;

   /* Reset vf_OwnPhysical for all VolCBs mapped onto this physical drive */

   for (pVolCBx = VolCB_Head; pVolCBx != NULL; pVolCBx = pVolCBx->pNextVolCB)

      if (pVolCB->PhysDriveNum == pVolCBx->PhysDriveNum)
         pVolCBx->Flags &= ~vf_OwnPhysical;


   /* Set ownership for this logical drive */

   pVolCB->Flags |= vf_OwnPhysical | vf_Changed;


  if ( (NumRemovableDisks == 1) && (pVolCB->LogDriveNum <= 1) )
     XActPDrv = pVolCB->LogDriveNum;

}







/*------------------------------------------------------------------------
;
;** CheckChangeSignal - Check floppy change signal
;
;   Checks the floppy change signal.
;
;   USHORT CheckChangeSignal (USHORT LogDriveNum, NPVOLCB pVolCB)
;
;   ENTRY:    LogDriveNum      - Logical Drive Number
;             pVolCB           - Pointer to VolCB
;
;   RETURN:   USHORT           - Media Change status as follows:
;                                   -1 = Media has been changed
;                                    0 = Unsure if media has been changed
;                                    1 = Media unchanged
;
------------------------------------------------------------------------*/
USHORT CheckChangeSignal (LogDriveNum, pVolCB)

USHORT   LogDriveNum;
NPVOLCB  pVolCB;

{
   USHORT ChangeState;
   PRP_INTERNAL pIRP;

   if (DevHelp_AllocReqPacket(0, (PBYTE FAR *) &pIRP) != NO_ERROR)
      return(STDON + STERR + ERROR_I24_GEN_FAILURE);

   pIRP->rph.Cmd = CMDInternal;
   pIRP->rph.Unit = LogDriveNum;
   pIRP->rph.Flags = RPF_Internal;
   pIRP->Function = DISKOP_GET_CHANGELINE_STATE;
   pIRP->SectorSize = pVolCB->MediaBPB.BytesPerSector;                          

   DiskIO_Wait( (PBYTE)pIRP, pVolCB);

   if (pIRP->rph.Status & STERR)
      ChangeState = MEDIA_UNSURE_CHANGED;
   else
   {
      if (pIRP->RetStatus & US_CHANGELINE_ACTIVE)
         ChangeState = MEDIA_CHANGED;
      else
         ChangeState = MEDIA_UNCHANGED;
   }

   DevHelp_FreeReqPacket((PBYTE)pIRP);        /* Free internal RP   */

   return(ChangeState);

}

/*------------------------------------------------------------------------
;
;** CheckWithinPartition - Check I/O falls within partition limits
;
;   Verifies I/O fails within parition limits.
;
;   USHORT CheckWithinPartition (NPVOLCB pVolCB, ULONG rba, ULONG NumSectors)
;
;   ENTRY:    pVolCB           - Pointer to VolCB
;             rba              - rba
;             NumSectors       - Number of sectors to transfer
;
;   RETURN:   USHORT           - Packet status word
;
------------------------------------------------------------------------*/
USHORT CheckWithinPartition (pVolCB, rba, NumSectors)

NPVOLCB pVolCB;
ULONG   rba;
ULONG   NumSectors;

{
   ULONG EndSector;

   /* Check for an assigned partition */                             /*@V187707*/
                                                                     /*@V187707*/
   if (pVolCB->Flags & vf_NoAssignedPartition)                       /*@V187707*/
      return(STDON + STERR + ERROR_I24_NOT_READY);                   /*@V187707*/

   /* Check for a valid partition */

   if (pVolCB->Flags & (vf_TooBig | vf_NoDOSPartition))
      return(STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);

   /* Make sure I/O doesnt go beyond end of partition */

   if ( (EndSector = f_add32 (rba, NumSectors)) == 0)
      return(STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);

   if (pVolCB->MediaBPB.TotalSectors != 0)
   {
      if (EndSector > pVolCB->MediaBPB.TotalSectors)
          return(STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);
   }
   else
   {
      if (EndSector > pVolCB->MediaBPB.BigTotalSectors)
          return(STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);
   }
   return(STDON);
}

/*------------------------------------------------------------------------
;
;** CHS_to_RBA - Convert Cylinder, Head, Sector to RBA
;
;   Converts a Cylinder/Head/Sector address to a double word
;   Relative Block Address using this formula:
;
;   RBA = (((Cylinder * NumHeads) + Head) * SectorsPerTrack) + (Sector - 1)
;
;   ULONG  NEAR CHS_to_RBA   (NPVOLCB pVolCB, USHORT Cylinder,
;                             UCHAR Head, UCHAR Sector)
;
;   ULONG  FAR  f_CHS_to_RBA (NPVOLCB pVolCB, USHORT Cylinder,
;                             UCHAR Head, UCHAR Sector)
;
;   ENTRY:    pVolCB           - VolCB entry for the device
;             Cylinder         - Cylinder
;             Head             - Head
;             Sector           - Sector
;
;   RETURN:   ULONG            - rba
;
;   EFFECTS:
;
;   NOTES:
------------------------------------------------------------------------*/

ULONG FAR f_CHS_to_RBA (pVolCB, Cylinder, Head, Sector)

NPVOLCB   pVolCB;
USHORT    Cylinder;
UCHAR     Head;
UCHAR     Sector;
{
   return(CHS_to_RBA (pVolCB, Cylinder, Head, Sector));
}

ULONG CHS_to_RBA (pVolCB, Cylinder, Head, Sector)

NPVOLCB   pVolCB;
USHORT    Cylinder;
UCHAR     Head;
UCHAR     Sector;
{
  ULONG rba;

  rba = (((((ULONG) Cylinder * pVolCB->MediaBPB.NumHeads) + Head) *
            pVolCB->MediaBPB.SectorsPerTrack) + Sector - 1);

  return (rba);
}


/*------------------------------------------------------------------------
;
;** MapIORBError - Maps an IORB error code to an ERROR_I24 error code.
;
;   Maps an IORB error code to an ERROR_I24 error code.
;
;   UCHAR  MapIORBError (USHORT IORBErrorCode)
;
;   ENTRY:    IORBError        - IORB error code
;
;   RETURN:   UCHAR            - ERROR_I24 error code
;
------------------------------------------------------------------------*/
typedef struct _ERROR_TABLE_ENTRY
{
   USHORT   IORB_ErrorCode;
   UCHAR    I24_ErrorCode;
} ERROR_TABLE_ENTRY;


UCHAR MapIORBError(IORB_ErrorCode)

USHORT IORB_ErrorCode;

{
   static ERROR_TABLE_ENTRY ErrorTable[] =
   {
      {IOERR_UNIT_NOT_READY,        ERROR_I24_NOT_READY},
      {IOERR_RBA_ADDRESSING_ERROR,  ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_RBA_LIMIT,             ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_RBA_CRC_ERROR,         ERROR_I24_CRC},
      {IOERR_MEDIA_NOT_FORMATTED,   ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_MEDIA_NOT_SUPPORTED,   ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_MEDIA_WRITE_PROTECT,   ERROR_I24_WRITE_PROTECT},
      {IOERR_MEDIA_CHANGED,         ERROR_I24_UNCERTAIN_MEDIA},
      {IOERR_MEDIA_NOT_PRESENT,     ERROR_I24_NOT_READY},
      {IOERR_DEVICE_NONSPECIFIC,    ERROR_I24_NOT_READY},
      {-1,-1},
   };

   USHORT i;

   /* Map the IORB error to the corresponding ERROR_I24 error code */

   for (i = 0; ErrorTable[i].IORB_ErrorCode != -1; i++)
     if (ErrorTable[i].IORB_ErrorCode == IORB_ErrorCode)
        return(ErrorTable[i].I24_ErrorCode);

   return(ERROR_I24_GEN_FAILURE);
}


/*------------------------------------------------------------------------
;
;** VirtToPhys - Convert a virtual to physical address
;
;   Convert a virtual to a physical address.  This routine does
;   not use DevHlp_VirtToPhys since that devhlp is not callable
;   at interrupt time.
;
;   ULONG  VirtToPhys  (PBYTE VirtAddr)
;
;   ENTRY:    VirtAddr         - 16:16 virutal address
;
;   RETURN:   ULONG            - 32 bit phys address
;
------------------------------------------------------------------------*/
ULONG FAR f_VirtToPhys (VirtAddr)

PBYTE VirtAddr;
{
   return(VirtToPhys (VirtAddr));
}


ULONG VirtToPhys (VirtAddr)

PBYTE VirtAddr;

{
  USHORT rc;
  SCATGATENTRY ScatGatEntry;
  ULONG VirtLinAddr;
  ULONG ScatLinAddr;
  ULONG PageListCount;

  rc = DevHelp_VirtToLin((USHORT) (SELECTOROF(VirtAddr)),
                         (ULONG) (OFFSETOF(VirtAddr)),
                         (PLIN) &VirtLinAddr);

  rc = DevHelp_VirtToLin((USHORT) ( ((ULONG)((PVOID) &ScatGatEntry)) >> 16),
                         (ULONG)  ( (USHORT)((PVOID) &ScatGatEntry)),
                         (PLIN) &ScatLinAddr);

  rc = DevHelp_LinToPageList(VirtLinAddr, 1, ScatLinAddr,
                                             (PULONG) &PageListCount);

  return(ScatGatEntry.ppXferBuf);

}





USHORT PowerOf2 (value)

UCHAR  value;
{
   if (value == 0)
      return(0);
   else
      return(1);
}


