/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = OPIOCTL.C
 *
 * DESCRIPTIVE NAME = IOCTL handling routines for OS/2 Optical Device Mgr
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "oph.h"

typedef struct _IOCTL_TABLE_ENTRY
{
   USHORT Function;
   USHORT (NEAR *pIOCTL_Routine)(PRP_GENIOCTL, NPUNITCB);
   UCHAR  ParamPktLen;
   UCHAR  DataPktLen;
} IOCTL_TABLE_ENTRY;


typedef struct _LOCKSTRUC
{
  USHORT flags;
  ULONG  lin_lock_struc;
  UCHAR  lockhandle_param[12];
  UCHAR  lockhandle_data[12];
} LOCKSTRUC, FAR * PLOCKSTRUC;


#define PARM_PACKET_LOCKED   1
#define DATA_PACKET_LOCKED   2

#define MO 0

/****************************************************************************
 *
 * FUNCTION NAME = f_DriveGenIOCTL
 *
 * DESCRIPTION   = Category 8/80/81 IOCTL routines
 *
 *          Category 8/80/81 IOCTL routines and IOCTL function router.
 *
 *          USHORT f_DriveGenIOCTL (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR f_DriveGenIOCTL ( PRP_GENIOCTL  pRP, NPUNITCB      pUnitCB)

{
   /*
   ** IOCTL Command Router Tables
   */

   /*
   ** Category 8 - Logical Disk Control Command Router Table
   */
    static IOCTL_TABLE_ENTRY Cat8_IOCTL_Table[] =
    {
/* 40 ok*/     {IODC_RC,          RemovableMediaControl,
                                  sizeof(DDI_DsktRemovMediaCtl_param),
                                  0 },

/* 43 ok*/     {IODC_SP,          SetDeviceParms8,
                                  sizeof(DDI_DeviceParameters_param),
                                  0},

/* 44 */     {IODC_WT,            ReadWriteVerifyTrack,
                                  sizeof(DDI_RWVPacket_param),
                                  -1},

/* 45 ok*/     {IODC_FT,          FormatTrack,
                                  sizeof(DDI_FormatPacket_param),
                                  0 },

/* 63 ok*/     {IODC_GP,          GetDeviceParms8,
                                  sizeof(DDI_DeviceParameters_param),
                                  sizeof(DDI_DeviceParameters_data) },

/* 64 */   {IODC_RT,              ReadWriteVerifyTrack,
                                  sizeof(DDI_RWVPacket_param),
                                  -1},

/* 65 */   {IODC_VT,              ReadWriteVerifyTrack,
                                  sizeof(DDI_RWVPacket_param),
                                  0 },

/* 66 ok*/     {IODC_ST,          RemovableDeviceStatus,
                                  0,
                                  sizeof(DDI_DsktRemovMediaCtl_data)},
#if MO
/* 67 ok*/     {IODC_WP,          QueryWriteProtect,
                                  0,
                                  sizeof(DDI_DsktRemovMediaCtl_data) },
#endif

     {-1},                // End of Table
    };

    static IOCTL_TABLE_ENTRY Cat9_IOCTL_Table[] =
    {
/* 63 ok*/     {IODC_GP,            GetDeviceParms9,
                                    sizeof(DDI_DeviceParameters_param),
                                    sizeof(DDI_PhysDeviceParameters_data)},

     {-1},                // End of Table
    };

    /*
    ** Category 80 - CD-ROM IOCTL Command Router Table
    */
    static IOCTL_TABLE_ENTRY Cat80_IOCTL_Table[] =
    {

/* 40 ok*/     {IOCD_LOCK_UNLOCK,   LockUnlock,
                                    sizeof(struct LockUnlock),
                                    0 },

/* 41 ok*/     {IOCD_EJECT,         EjectDisk,
                                    sizeof(struct EjectDisk),
                                    0 },

/* 42 */       {IOCD_CLOSE_TRAY,    CloseTray,
                                    sizeof(struct CloseTray),
                                    0 },
#if MO
//  query partition info
/* 43 */       {IOCD_QUERY_PARTITION,    CloseTray,
                                    sizeof(struct CloseTray),
                                    0 },
#endif



/* 4F */       {IOCD_WRITE_CTRL,    WriteCtrl,
                                    sizeof(struct DDI_CtrlString),
                                    0},


/* 60 ok*/     {IOCD_DEVICE_STATUS, DeviceSupport,
                                    sizeof(struct QueryDeviceSupport),
                                    sizeof(struct QueryDeviceSupportData)},
#if MO
//Erase Media
/* 61 ok*/     {IOCD_ERASE_MEDIA,   ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    0},
//Write no erase
/* 62 ok*/     {IOCD_WRITE_NOERASE, ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    -1},
// Write long
/* 63 ok*/     {IOCD_WRITE_LONG,    ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    -1},
// Read long
/* 64 ok*/     {IOCD_READ_LONG,     ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    -1},
// Media Scan
/* 66 ok*/     {IOCD_MEDIA_SCAN,    ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    sizeof(ULONG)},
// Media Scan 2
/* 67 ok*/     {IOCD_MEDIA_SCAN2,   ReadWriteEraseScan,
                                    sizeof(DDI_RWVPacket_param),
                                    sizeof(ULONG)},
#endif

/* 6f */       {IOCD_READ_CTRL,     ReadCtrl,
                                    sizeof(struct DDI_CtrlString),
                                    0},

     {-1},                // End of Table
    };
    static IOCTL_TABLE_ENTRY Cat87_IOCTL_Table[] =
    {
     {IOCD_CHANGE_TIMEOUT_VALUE,    ChangeTimeout,
                                    sizeof(struct UserChangeTimeout),
                                    sizeof(struct UserChangeTimeoutData)},

     {-1},                // End of Table
    };


   /*
   ** Verify it's an IOCTL the driver supports, verify
   ** the parameter and data packets, and route the
   ** the request to it's command handler.
   */

   USHORT  rc, Unit, i;
   USHORT (NEAR *pIOCTL_Routine)(PRP_GENIOCTL, NPUNITCB);
   IOCTL_TABLE_ENTRY *pTable;

   LOCKSTRUC lock_struc;


   switch (pRP->Category)
   {
      case IOC_DC:
         pTable = Cat8_IOCTL_Table;
         break;

      case IOC_PD:
         pTable = Cat9_IOCTL_Table;
         break;

      case IOC_CDROM:
         pTable = Cat80_IOCTL_Table;
         break;
      case IOC_OPTICAL:
         pTable = Cat87_IOCTL_Table;
         break;

      default:
         return (STERR + STDON + ERROR_I24_BAD_COMMAND);
   }

   /*
   ** Verify it's an IOCTL Function the  driver supports
   ** and get the entry point of the IOCTL function to call
   */
   for (i = 0; pTable[i].Function != -1; i++)
   {
      if (pRP->Function == pTable[i].Function)
      {
         pIOCTL_Routine = pTable[i].pIOCTL_Routine;
         break;
      }
   }

   if (pTable[i].Function == -1)
      return (STERR + STDON + ERROR_I24_BAD_COMMAND);

   /*
   ** Valid Category and Command Code.  Now validate access to the
   ** parameter and data packets.  Lock down the data packet if required.
   */
   Unit = pRP->rph.Unit;

   lock_struc.flags = 0;

   if (VerifyParameters(pUnitCB,pRP, pTable[i].ParamPktLen,
                             pTable[i].DataPktLen,
                             (LOCKSTRUC FAR *) &lock_struc) == STDON)

      /*
      ** Invoke the command handler for the IOCTL
      */
   {
      rc = (*pIOCTL_Routine)(pRP, pUnitCB);
   }
   else
   {
      rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   }

   if (lock_struc.flags & PARM_PACKET_LOCKED)
   {
         DevHelp_VMUnLock (lock_struc.lin_lock_struc +
            (ULONG)(FIELDOFFSET(LOCKSTRUC, lockhandle_param[0])));  /*@V133532*/
   }

   if (lock_struc.flags & DATA_PACKET_LOCKED)
   {
         DevHelp_VMUnLock (lock_struc.lin_lock_struc +
            (ULONG)(FIELDOFFSET(LOCKSTRUC, lockhandle_data[0])));   /*@V133532*/
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = VerifyParameters
 *
 * DESCRIPTION   = Verify Parameter and Data Packet Pointers
 *
 *       Verify access to the Parameter and Data Packet pointer is the
 *       IOCTL request packet.  If the request is for an operation where
 *       data will be DMA'ed directly into the data packet, then issue a
 *       a long term lock on the data packet.
 *
 *       USHORT VerifyParameters (PRP_GENIOCTL pRP, USHORT ParmPktLen,
 *                                                  USHORT DataPktLen,
 *                                                  PBYTE plock_struc,
 *
 * INPUT         = pRP              - Request Packet
 *                 ParamPktLen      - Length of Parameter Packet
 *                 DataPktLen       - Length of Data Packet
 *                 plock_struc      - address of lock structure
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT VerifyParameters ( NPUNITCB pUnitCB,
                          PRP_GENIOCTL pRP,
                          USHORT       ParamPktLen,
                          USHORT       DataPktLen,
                          PLOCKSTRUC   plock_struc)
{
   USHORT rc,flag;
   ULONG lin_Pkt;                /* Linear address of Parm or Data Packet */
   ULONG lin_lock_struc;
   ULONG lin_lockhandle_param;
   ULONG lin_lockhandle_data;
   ULONG PageListCount;
   ULONG Length;
   ULONG VMLockFlags;


   rc = 0;

   /*
   ** Verify the string CD01 appears at the beginning of each Cat 80
   ** and Cat 81 parameter packet.
   */

   /*
   ** Get linear address of lock structure on the stack
   */
   DevHelp_VirtToLin(SELECTOROF(plock_struc),
                    (ULONG) OFFSETOF(plock_struc),
                    (PVOID) &lin_lock_struc);

   plock_struc->lin_lock_struc = lin_lock_struc;

/*
** lin_pagelist =  lin_lock_struc +
**                 (ULONG) ((USHORT) &(plock_struc->page_list[0]) -
**                          (USHORT) &(plock_struc));
*/
   /*
   ** Verify access to the parameter packet
   */
   if (ParamPktLen != 0)
   {
      /*
      ** Get linear address of the Parameter Packet
      */
      rc = DevHelp_VirtToLin(SELECTOROF(pRP->ParmPacket),
                            (ULONG) OFFSETOF(pRP->ParmPacket),
                            (PVOID) &lin_Pkt);
      if (rc == 0)
      {
         lin_lockhandle_param = lin_lock_struc +
                       (ULONG) ((USHORT) &(plock_struc->lockhandle_param[0]) -
                                (USHORT) &(plock_struc->flags));

         if ((rc = DevHelp_VMLock(VMDHL_LONG | VMDHL_VERIFY,
                                  lin_Pkt,
                                  ParamPktLen,
                                  -1L,
                                  lin_lockhandle_param,
                                  (PULONG) &PageListCount)) == 0)
         {
            plock_struc->flags = PARM_PACKET_LOCKED;
         }
      }
   }

   /*
   ** Verify access to the data packet.  If it's a ReadLong, then
   ** long term lock and make contig the data packet.
   */
   flag=VMDHL_VERIFY;
   if(DataPktLen==(UCHAR)-1)
     {
     flag =VMDHL_CONTIGUOUS;
     DataPktLen=((PDDI_RWVPacket_param)pRP->ParmPacket)->NumSectors*pUnitCB->CurrentBPB.BytesPerSector;
     // if owning adapter doesn't support >16meg
     // make sure the buffer is locked BELOW the 16 meg line
     if(!(pUnitCB->Flags & UCF_16M))
       flag |= VMDHL_16M;
     } /* endif */
   if (rc == 0 && DataPktLen != 0)
   {
      rc = DevHelp_VirtToLin(SELECTOROF(pRP->DataPacket),
                       (ULONG) OFFSETOF(pRP->DataPacket),
                       (PVOID) &lin_Pkt);
      if (rc == 0)
      {
         lin_lockhandle_data = lin_lock_struc +
                       (ULONG) ((USHORT) &(plock_struc->lockhandle_data[0]) -
                                (USHORT) &(plock_struc->flags));

         Length = DataPktLen;
         VMLockFlags = VMDHL_WRITE | VMDHL_LONG | flag;


         rc = DevHelp_VMLock(VMLockFlags, lin_Pkt, Length, -1L,
                     lin_lockhandle_data, (PULONG) &PageListCount);
      }

      if (rc == 0)
      {
         plock_struc->flags |= DATA_PACKET_LOCKED;
      }
      else
      {
         if (plock_struc->flags & PARM_PACKET_LOCKED)
         {
            DevHelp_VMUnLock(lin_lockhandle_param);
            plock_struc->flags = 0;
         }
      }
   }

   if (rc)
      rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   else
      rc = STDON;

   return(rc);
}

/******************************************************************************
*
*   SUBROUTINE NAME:    CalculateSectorsPerTrack
*
*   DESCRIPTIVE NAME:   CalculateSectorsPerTrack
*
*   FUNCTION:   Calculate minimum SectorsPerTrack from media size
*
*   NOTES:
*
*   ENTRY POINT:    CalculateSectorsPerTrack
*       LINKAGE:    call near
*
*   PARAMETERS:  volume_size
*
*   EXIT-NORMAL:    status = DONE
*
*   EXIT-ERROR:     none
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:
*       DATA:
*
*   PSEUDOCODE:
*
*       Calculate minimum possible sectors per track
*       If 100 to 128 meg media, use erimo size (25) for compatibility
*       Else use the minimum possible multiple of 32
*
******************************************************************************/

USHORT CalculateSectorsPerTrack ( ULONG    volume_size )
{
    register
    USHORT    sectors_per_track;
    register
    USHORT    min_sectors_per_track;

    min_sectors_per_track = HIUSHORT ( volume_size );   // right shift by 16
                                                        //  divided it by 64 K.
    if ( min_sectors_per_track == 3 ) {                 // 100 to 128 meg disk
        sectors_per_track = ERIMO_SECTORS_PER_TRACK;
    } else {                                            // all others
        sectors_per_track = SECTORS_PER_TRACK;
        while ( sectors_per_track < min_sectors_per_track ) {
            sectors_per_track *= 2;
        }
    }

    return ( sectors_per_track );
}

/******************************************************************************
*
*   SUBROUTINE NAME:    CalculateClusterSize
*
*   DESCRIPTIVE NAME:   CalculateClusterSize
*
*   FUNCTION:   Calculate minimum Cluster Size from media size
*
*   NOTES:
*
*   ENTRY POINT:    CalculateClusterSize
*       LINKAGE:    call near
*
*   PARAMETERS:  volume_size
*
*   EXIT-NORMAL:    status = DONE
*
*   EXIT-ERROR:     none
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:
*       DATA:
*
*   PSEUDOCODE:
*
*       Cluster size is 1 for <= 32 Meg media size
*       Cluster size is 2 for <= 64 Meg media size
*       Cluster size is 4 for <= 128 Meg media size
*       ...
*
******************************************************************************/

USHORT CalculateClusterSize ( ULONG    volume_size )
{                                           // 1 sector/cluster = 32 Meg size
    register
    USHORT    cluster_size;
    register
    USHORT    power_of_2;

    cluster_size = HIUSHORT ( volume_size );    // right shift by 16
                                                    // divided it by 64 K
                                                    //  sectors == 32 Meg bytes

    if ( cluster_size < 1 ) {
        cluster_size = 1;
    } else {
        power_of_2 = 0;
        while ( cluster_size > 0 ) {
            ++power_of_2;
            cluster_size >>= 1;                 // count up power of 2
        }
        cluster_size = 1;
        cluster_size <<= power_of_2;            // next higher power of 2
    }

    return ( cluster_size );
}

/******************************************************************************
*
*   SUBROUTINE NAME:    CalculateSectorsPerFat
*
*   DESCRIPTIVE NAME:   CalculateSectorsPerFat
*
*   FUNCTION:   Calculate SectorsPerFat from media and cluster size
*
*   NOTES:
*               assumes 512 sector size,
*               2 fats, 1 reserved sector.
*
*   ENTRY POINT:    CalculateSectorsPerFat
*       LINKAGE:    call near
*
*   PARAMETERS:  volume_size
*                cluster_size
*
*   EXIT-NORMAL:    SectorsPerFat
*
*   EXIT-ERROR:     none
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:
*       DATA:
*
*   PSEUDOCODE:
*
*       SPF = (TS - 1 - (root directory size * 32 / sector size) /
*             (2 + (sector size * SPCluster / 2))
*       (int Div, so round it up by 1)
*
******************************************************************************/

USHORT CalculateSectorsPerFat ( ULONG  volume_size,
                         ULONG    cluster_size,
                         ULONG    sector_size )
{
    return ( 1 +  (volume_size - 1 - ( (512 * 32) / sector_size) ) /
                       (2 + ((sector_size * cluster_size)/2)) );
}
/******************************************************************************
*
*   SUBROUTINE NAME:    CopyDefaultBPB
*
*   DESCRIPTIVE NAME:   Copy Default BPB
*
*   FUNCTION:   Copy a BPB to the supplied pointer
*
*   NOTES:
*
*   ENTRY POINT:    CopyDefaultBPB
*       LINKAGE:    call near
*
*   PARAMETERS:
*                unit
*                *bpb_area
*
*   EXIT-NORMAL:    total_tracks
*
*   EXIT-ERROR:     none
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:   DevHelp_VerifyAccess
*       DATA:       Bpb
*
*   PSEUDOCODE:
*
*       CopyDefaultBPB will construct a bpb if it can get_device_capacity
*        from the drive. Otherwise, it uses the default erimo bpb.
*       Move data to user data area
*       Return total_tracks ( = total_cylinders for 1 head )
*
******************************************************************************/

USHORT CopyDefaultBPB ( NPUNITCB pUnitCB, BPB *pBPB)
{
    USHORT    rc;
    USHORT    n;
    BPB far   *p_bpb;
    ULONG   volume_size;
    ULONG   total_tracks;
    ULONG   sector_size;
    BPB     bpb;

    rc = GetVolumeSize (pUnitCB, &volume_size,&sector_size);

    total_tracks = NUMBER_OF_CYLINDERS;             // preset default

    if ( (rc & STERR)  ||  volume_size == 0L || volume_size == -1L )
      {
        p_bpb =  &ErimoDefaultBPB;          // error, default = erimo
      }
    else
      {
      p_bpb = &bpb;                      // constuct bpb

      bpb.BytesPerSector = sector_size;
      bpb.SectorsPerCluster = CalculateClusterSize ( volume_size );
      bpb.ReservedSectors = RESERVED_SECTORS;
      bpb.NumFATs = NUMBER_OF_FATS;
      bpb.MaxDirEntries = DIRECTORY_SIZE;
      bpb.TotalSectors = TOTAL_SECTORS;
      bpb.MediaType = MEDIA_DESCRIPTOR;
      bpb.NumFATSectors = CalculateSectorsPerFat ( volume_size,
                                                     bpb.SectorsPerCluster,
                                                     (USHORT)sector_size );
      bpb.SectorsPerTrack = CalculateSectorsPerTrack ( volume_size );
      bpb.NumHeads = NUMBER_OF_HEADS;
      bpb.HiddenSectors = HIDDEN_SECTORS;

      total_tracks =   volume_size / bpb.SectorsPerTrack ;
      bpb.BigTotalSectors = total_tracks*bpb.SectorsPerTrack;
      }

    *pBPB = *p_bpb;

    return ( total_tracks );

}

/****************************************************************************
 *
 * FUNCTION NAME = GetDeviceParms8
 *
 * DESCRIPTION   = Get Device Parms
 *
 *       Transfers the BPB, Number of Cylinders, Device Type and Device
 *       attributes to the IOCTL data packet.
 *
 *       USHORT GetDeviceParms8(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetDeviceParms8 ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{
   PDDI_DeviceParameters_param pParmPkt;
   PDDI_DeviceParameters_data pDataPkt;
   USHORT rc,cluster_size,sectors_per_track;
   ULONG volume_size,sector_size;
   BPB  *bpb;

   pParmPkt = (PDDI_DeviceParameters_param) pRP->ParmPacket;
   pDataPkt = (PDDI_DeviceParameters_data) pRP->DataPacket;

   if (pParmPkt->Command > COMMAND_INFO_MASK)
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   pDataPkt->DeviceType = TYPE_OPTICAL;

   pDataPkt->DeviceAttr = 0;

   if ( !(pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) )
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_NON_REMOVABLE;

//   if (pUnitCB->UnitInfo.UnitFlags & UF_CHANGELINE)
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_CHANGELINE;

//   if (pUnitCB->Flags & UCF_16M)
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_GT16MBSUPPORT;

   if ( pParmPkt->Command & BUILD_DEVICE_BPB )
     {
     rc = ReadSector (pUnitCB, 0, 1,(ULONG)ppDataSeg+((USHORT)&BootRecord), 512,FALSE );

     if(rc==STDON)
       {
       if ( VerifyBPB ( (BPB far *)&BootRecord.MediaBPB ) )
         {
         pDataPkt->NumCylinders = BootRecord.MediaBPB.BigTotalSectors/
                                     BootRecord.MediaBPB.SectorsPerTrack;
         bpb = &BootRecord.MediaBPB;
         }
       else
         {
         bpb = InitBPBArray[pRP->rph.Unit];
         pDataPkt->NumCylinders = CopyDefaultBPB ( pUnitCB, bpb );
         }
       } /* endif */
     else
       {
//       bpb = InitBPBArray[pRP->rph.Unit];
//       pDataPkt->NumCylinders = CopyDefaultBPB ( pUnitCB, bpb );
       return rc;
       } /* endelse */
     }
   else
     {
     pDataPkt->NumCylinders = NUMBER_OF_CYLINDERS;
     bpb = InitBPBArray[pRP->rph.Unit];
     if (!pUnitCB->DeviceInfo.default_bpb_changed )
       {
       pDataPkt->NumCylinders = CopyDefaultBPB ( pUnitCB, bpb );
       }
     }

   pUnitCB->CurrentBPB = *bpb;

   pDataPkt->bpb = pUnitCB->CurrentBPB;

   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = GetDeviceParms9
 *
 * DESCRIPTION   = Cat 9 Get Device Parms
 *
 *           Transfer physical device paramaters
 *
 *           USHORT GetDeviceParms9(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetDeviceParms9 ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{
   PDDI_PhysDeviceParameters_data pDataPkt;

   pDataPkt = (PDDI_PhysDeviceParameters_data) pRP->DataPacket;

   pDataPkt->NumCylinders = DEFAULT_CYLINDER_COUNT;
   pDataPkt->NumHeads = NUMBER_OF_HEADS;
   pDataPkt->SectorsPerTrack = SECTORS_PER_TRACK;

   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = RemovableMediaControl
 *
 * DESCRIPTION   = Removable Media Control
 *
 *      Cat 8 function to allow lock, unlock or eject of media in the drive.
 *      This function simply calls the equivalent Cat 80 function.
 *
 *      USHORT RemovableMediaControl(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT RemovableMediaControl ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{
   USHORT rc;
   PDDI_DsktRemovMediaCtl_param pParmPkt;

   pParmPkt = (PDDI_DsktRemovMediaCtl_param) pRP->ParmPacket;

   switch (pParmPkt->Command)
   {
      case UNLOCK_MEDIA:
      case LOCK_MEDIA:
        rc = LockUnlock(pRP, pUnitCB);
        break;

      case EJECT_MEDIA:
        rc = EjectDisk(pRP, pUnitCB);
        break;

      case LOAD_MEDIA:                                              /*@V133532*/
        rc = CloseTray(pRP, pUnitCB);                            /*@V133532*/
        break;                                                      /*@V133532*/

      default:
         rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = RemovableDeviceStatus
 *
 * DESCRIPTION   = return device status
 *
 *        Cat 8 function to return removable media status
 *
 *        USHORT RemovableDeviceStatus (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT RemovableDeviceStatus ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{
   NPIORB_CDB pIORB;
   ULONG status,volume_size,sector_size;
   USHORT junk;
   PDDI_DsktRemovMediaCtl_data pDataPkt;
   struct CDB_ModeSense_6  NEAR *pCDB;
   PBYTE p;
   struct ModeSenseHeader far *pHeader;
   struct ModeSensePage0 far *pMode=0;

   pDataPkt = (PDDI_DsktRemovMediaCtl_data) pRP->DataPacket;

   status = 3;                    /* Lock supported, but lock status isnt */

#if 0
   BuildCDB_TestUnitReady (pUnitCB, (NPIORB_CDB FAR *) &pIORB);

   if  (SubmitIORB_Wait (pUnitCB, pIORB) == STDON)
      status |= MEDIA_IN_DRIVE;

   FreeIORB (pUnitCB, pIORB);
#else
   if(GetVolumeSize (pUnitCB, &volume_size,&sector_size)==STDON)
     {
     status |= MEDIA_IN_DRIVE;
     }
#endif
   if(pUnitCB->DeviceInfo.lock_status_supported)
     {
     BuildCDB_GetLockStatus (pUnitCB, (NPIORB_CDB FAR *) &pIORB);
     if  (SubmitIORB_Wait (pUnitCB, pIORB) == STDON)
       {
       if(!DevHelp_PhysToVirt(pIORB->apt.pSGList->ppXferBuf,
                              sizeof(struct ModeSensePage0MaxSize),
                              (PVOID)&pHeader,&junk))
         {
//        _asm { int 3 };
         p=(PBYTE)pHeader;
         p+=pHeader->block_descriptor_length+sizeof(*pHeader);
         pMode=(struct ModeSensePage0 far *)p;
         if(pMode->flags & PRVNT_BIT)
           {
           status &= ~DRIVE_IS_UNLOCKED;
           } /* endif */
         else
           {
           status &= ~DRIVE_IS_LOCKED;
           } /* endelse */
         } /* endif */
       }
     FreeIORB (pUnitCB, pIORB);
     } /* endif */

   pDataPkt->Status = status;

   return(STDON);
}

/****************************************************************************
 *
 * FUNCTION NAME = ChangeTimeout
 *
 * DESCRIPTION   = change lasertimeout
 *
 *        Cat 8 function to
 *
 *        USHORT ChangeTimeout (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR ChangeTimeout ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{
    struct UserChangeTimeout far *pNewTimeout=(struct UserChangeTimeout far *)pRP->ParmPacket;
    struct UserChangeTimeoutData far *pOldTimeout=(struct UserChangeTimeoutData far *)pRP->DataPacket;
    pOldTimeout->previous_timeout_value = pUnitCB->DeviceInfo.FormatTime;
    pUnitCB->DeviceInfo.FormatTime=pNewTimeout->new_timeout_value;
    return (STDON);
}


USHORT NEAR ReadWriteVerifyTrack ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{

    USHORT rc;

    ULONG data_area;
    ULONG startSector;
    ULONG LockHandle;

    PDDI_RWVPacket_param param=(PDDI_RWVPacket_param)pRP->ParmPacket;

    startSector=((ULONG)pUnitCB->CurrentBPB.SectorsPerTrack*param->Cylinder)+param->FirstSector;

    if( pUnitCB->Flags & UCF_UNCERTAIN_MEDIA )
      return(STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA);

    if( param->Command != 1  ||
         param->Head != 0  ||
         param->NumSectors == 0  ||
         param->FirstSector + param->NumSectors > pUnitCB->CurrentBPB.SectorsPerTrack )
      return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );

      data_area = 0;

      switch(pRP->Function)
        {
        case IODC_WT:                 // 44
          DevHelp_VirtToPhys(pRP->DataPacket, (PULONG) &data_area);
          rc = WriteSector (pUnitCB,
                            startSector,
                            param->NumSectors,
                            data_area,
                            pUnitCB->CurrentBPB.BytesPerSector ,
                            FALSE);

          break;
        case IODC_RT:                 // 64
          DevHelp_VirtToPhys(pRP->DataPacket, (PULONG) &data_area);
          rc = ReadSector (pUnitCB,
                           startSector,
                           param->NumSectors,
                           data_area,
                           pUnitCB->CurrentBPB.BytesPerSector,
                           FALSE );
          break;
        case IODC_VT:                 // 65

          if ( pUnitCB->DeviceInfo.vendor_id_code == IBM )
            {
            DoModeSelect ( pUnitCB, PAGE_0 );     // enable verify realloc
            }
          rc = ReadSector (pUnitCB,
                           startSector,
                           param->NumSectors,
                           data_area,
                           pUnitCB->CurrentBPB.BytesPerSector,
                           TRUE );
          break;
        default:
         break;
        } /* endswitch */

    return rc;

}
#if MO
USHORT NEAR ReadWriteEraseScan ( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)

{

    USHORT rc;

    ULONG data_area;
    ULONG startSector;
    ULONG LockHandle;

    PDDI_RWVPacket_param param=(PDDI_RWVPacket_param)pRP->ParmPacket;

    startSector=((ULONG)pUnitCB->CurrentBPB.SectorsPerTrack*param->Cylinder)+param->FirstSector;

    if( pUnitCB->Flags & UCF_UNCERTAIN_MEDIA )
      return(STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA);

    if( param->Command != 1  ||
         param->Head != 0  ||
         param->NumSectors == 0  ||
         param->FirstSector + param->NumSectors > pUnitCB->CurrentBPB.SectorsPerTrack )
      return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );

      data_area = 0;

      switch(pRP->Function)
        {
        case IOCD_ERASE_MEDIA:
          break;
        case IOCD_WRITE_NOERASE:
          break;
        case IOCD_WRITE_LONG:
          break;
        case IOCD_READ_LONG:
          break;
        case IOCD_MEDIA_SCAN:
          break;
        case IOCD_MEDIA_SCAN2:
          break;
        case IODC_WT:                 // 44
          DevHelp_VirtToPhys(pRP->DataPacket, (PULONG) &data_area);
          rc = WriteSector (pUnitCB,
                            startSector,
                            param->NumSectors,
                            data_area,
                            pUnitCB->CurrentBPB.BytesPerSector ,
                            FALSE);

          break;
        case IODC_RT:                 // 64
          DevHelp_VirtToPhys(pRP->DataPacket, (PULONG) &data_area);
          rc = ReadSector (pUnitCB,
                           startSector,
                           param->NumSectors,
                           data_area,
                           pUnitCB->CurrentBPB.BytesPerSector,
                           FALSE );
          break;
        case IODC_VT:                 // 65

          if ( pUnitCB->DeviceInfo.vendor_id_code == IBM )
            {
            DoModeSelect ( pUnitCB, PAGE_0 );     // enable verify realloc
            }
          rc = ReadSector (pUnitCB,
                           startSector,
                           param->NumSectors,
                           data_area,
                           pUnitCB->CurrentBPB.BytesPerSector,
                           TRUE );
          break;
        default:
         break;
        } /* endswitch */

    return rc;

}
#endif

USHORT NEAR DoModeSelect(NPUNITCB pUnitCB, USHORT Type)
{

   NPIORB_CDB pIORB;
   USHORT rc;

   BuildCDB_ModeSelect (pUnitCB, Type, &pIORB);
   rc=SubmitIORB_Wait (pUnitCB, pIORB);
   FreeIORB (pUnitCB, pIORB);
   return rc;

}

USHORT NEAR FormatTrack( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)
{
   USHORT      rc=STERR;
   UCHAR       percent = 0;                    /* preset for 5.25 default */
   UCHAR       byte_1 = 0;
   ULONG       old_timeout_value;
   ULONG       new_timeout_value;
   ULONG       sector;
   USHORT      number_of_tracks;
   USHORT      cylinder;
   BOOL        force_format=FALSE;
   ULONG       defect_list_ptr=NULL;
   USHORT      defect_list_size=0;
   ULONG       defect_list;
   NPIORB_CDB  pIORB;

   PDDI_FormatPacket_param FormatParms=(PDDI_FormatPacket_param)pRP->ParmPacket;


    if( FormatParms->Command & FORCE_LOW_LEVEL_FORMAT )
      force_format = TRUE;

    if( force_format )
      {
      if ( FormatParms->Command != STANDARD_TRACK_LAYOUT + MULTI_TRACK_FORMAT +
                             FORCE_LOW_LEVEL_FORMAT )
          return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
      }
    else
      {
      if( FormatParms->Command != STANDARD_TRACK_LAYOUT + MULTI_TRACK_FORMAT )
          return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
      }

    number_of_tracks = FormatParms->NumTracks;
    cylinder = FormatParms->Cylinder;
    sector = cylinder;                                  // always valid LBA


    switch(pUnitCB->DeviceInfo.vendor_id_code)
      {
      case IBM:
        switch(pUnitCB->DeviceInfo.product_id_code)
          {
          case ERIMO1:
          case ERIMOPRIME:
          case TOPCAT:
            byte_1 = CL_BIT;
            percent = 1;
            break;
          default:
           break;
          } /* endswitch */
//      if( pUnitCB->DeviceInfo.product_id_code == ERIMO1 )
//        {
//        } /* endif */
        new_timeout_value = ERIMO_TOTAL_FORMATTING_TIME;
        if( !( force_format | ForceLowLevelFormat ))
          {
          rc = WriteSector (pUnitCB,
                        sector,
                        1,
                        (ULONG)ppDataSeg+((USHORT)&BootRecord),
                        pUnitCB->CurrentBPB.BytesPerSector ,
                        FALSE);
          }
        if( pUnitCB->DeviceInfo.needs_mode_select )
          {
          DoModeSelect ( pUnitCB, PAGE_20 );               // ignore rc
          }
        break;
      case PINNACLE:
        byte_1 = CL_BIT + FD_BIT;
        defect_list = 0x0000E400;
        DevHelp_VirtToPhys(&defect_list,  &defect_list_ptr);
        defect_list_size=sizeof(defect_list);

        new_timeout_value = ERIMO_TOTAL_FORMATTING_TIME;

        if( force_format | ForceLowLevelFormat )
          {
          if ( pUnitCB->DeviceInfo.format_unit_done )
            {
            rc = WriteSector (pUnitCB,
                          sector,
                          1,
                          (ULONG)ppDataSeg+((USHORT)&BootRecord),
                          pUnitCB->CurrentBPB.BytesPerSector ,
                          FALSE);
            }
          else
            {
            pUnitCB->DeviceInfo.format_unit_done = TRUE;
            }
          }
        else
          {
          rc = WriteSector (pUnitCB,
                      sector,
                      1,
                      (ULONG)ppDataSeg+((USHORT)&BootRecord),
                      pUnitCB->CurrentBPB.BytesPerSector ,
                      FALSE);
          }
        break;
      case SONY:
        percent = 2;
      default:
        new_timeout_value = TOTAL_FORMATTING_TIME;
        if( force_format | ForceLowLevelFormat )
          {
          if( pUnitCB->DeviceInfo.format_unit_done )
            {
            rc = WriteSector (pUnitCB,
                        sector,
                        1,
                        (ULONG)ppDataSeg+((USHORT)&BootRecord),
                        pUnitCB->CurrentBPB.BytesPerSector ,
                        FALSE);
            }
          else
            {
            pUnitCB->DeviceInfo.format_unit_done = TRUE;
            }
          }
        else
          {
          rc = WriteSector (pUnitCB,
                    sector,
                    1,
                    (ULONG)ppDataSeg+((USHORT)&BootRecord),
                    pUnitCB->CurrentBPB.BytesPerSector ,
                    FALSE);
          }
       break;
      } /* endswitch */

     if(rc != STDON)
       {
       BuildIORB_FormatUnit(pUnitCB,
                           byte_1,
                           percent,
                           defect_list_ptr,
                           defect_list_size,
                           (NPIORB_CDB far *)&pIORB);
       pIORB->apt.iorbh.Timeout=new_timeout_value;
       rc=SubmitIORB_Wait(pUnitCB, pIORB);
       FreeIORB (pUnitCB, pIORB);
       } /* endif */

    if(rc == STDON)
      {
      FormatParms->NumTracks = MULTI_TRACK_SUCCESSFUL;
      }
    else
      {
      FormatParms->NumSectors = FormatParms->NumTracks;
      FormatParms->NumTracks = MULTI_TRACK_FAILED;
      }

    return ( rc );


}

/******************************************************************************
*
*   SUBROUTINE NAME:    VerifyBPB
*
*   DESCRIPTIVE NAME:   Verify BPB
*
*   FUNCTION:   Verify the BPB
*
*   NOTES:
*
*   ENTRY POINT:    VerifyBPB
*       LINKAGE:    call near
*
*   PARAMETERS:
*                   far     *boot_record;
*
*   EXIT-NORMAL:    TRUE
*
*   EXIT-ERROR:     FALSE
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:
*       DATA:
*
*   PSEUDOCODE:
*
*       Check for impossible parameters in the BPB
*
******************************************************************************/

BOOL VerifyBPB ( BPB far *bpb )
{
    UCHAR   cluster_size;
    USHORT  sector_size;
    BOOL    verified = TRUE;

    if ( (bpb->MediaType & MEDIA_DESCRIPTOR) != MEDIA_DESCRIPTOR  ||
         bpb->SectorsPerTrack == 0  ||
         (bpb->BigTotalSectors == 0L && bpb->TotalSectors == 0) )
      {
      verified = FALSE;
      }
    else
      {
      cluster_size = bpb->SectorsPerCluster;

      while ( cluster_size > 0  &&  cluster_size != 1 )
          cluster_size >>= 1;

      if ( cluster_size != 1 )                    /* must be power of 2 */
          verified = FALSE;

      sector_size = bpb->BytesPerSector;

      while ( sector_size > 0  &&  sector_size != 1 )
          sector_size >>= 1;

      if ( sector_size != 1 )                    /* must be power of 2 */
          verified = FALSE;
      }

    return ( verified );
}
/******************************************************************************
*
*   SUBROUTINE NAME:    SetDeviceParameters
*
*   DESCRIPTIVE NAME:   Set Device Parameters
*
*   FUNCTION:   Change the BPB
*
*   NOTES:
*
*   ENTRY POINT:    SetDeviceParameters
*       LINKAGE:    call near
*
*   PARAMETERS:     rh  ( generic ioctl request header )
*
*   EXIT-NORMAL:    status = DONE
*
*   EXIT-ERROR:     none
*
*   EFFECTS:
*
*   PUBLIC REFERENCES:
*       ROUTINES:   DevHelp_VerifyAccess
*       DATA:       Bpb
*
*   PSEUDOCODE:
*
*       Verify access to parameter area
*       Verify access to data area
*       Perform the indicated function
*
******************************************************************************/


USHORT NEAR SetDeviceParms8( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)
{
    UCHAR   command;
    USHORT  rc;
    USHORT  n;

    PDDI_DeviceParameters_param pParmPkt;
    PDDI_DeviceParameters_data pDataPkt;
    BPB  *bpb;

    pParmPkt = (PDDI_DeviceParameters_param) pRP->ParmPacket;
    pDataPkt = (PDDI_DeviceParameters_data) pRP->DataPacket;


    if ( (command = pParmPkt->Command) > MAX_COMMAND_INFORMATION )
          return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );

    if( command > REVERT_TO_BPB_OFF_MEDIUM )
      {
      if(DevHelp_VerifyAccess ( SELECTOROF(pDataPkt), sizeof (DDI_DeviceParameters_data),OFFSETOF(pDataPkt),
                          VERIFY_READONLY ))
        {
        return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
        }
      if( ! VerifyBPB ( (BPB far *)&pDataPkt->bpb ) )
        {
        return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
        }
      }

    switch ( command )
      {
      case REVERT_TO_BPB_OFF_MEDIUM:
        pUnitCB->DeviceInfo.BuildBPBOffMedium = TRUE;
        break;
      case CHANGE_DEFAULT_BPB:
        bpb = InitBPBArray[pRP->rph.Unit];
        pUnitCB->DeviceInfo.default_bpb_changed ^= TRUE;
        break;
      case CHANGE_MEDIUM_BPB:
        bpb = &pUnitCB->CurrentBPB;
        pUnitCB->DeviceInfo.BuildBPBOffMedium = FALSE;
        pUnitCB->DeviceInfo.format_unit_done = FALSE;       // start of format
        break;
      }

    if ( command > REVERT_TO_BPB_OFF_MEDIUM )
      {
      *bpb = *((BPB far *)(&pDataPkt->bpb)) ;
      }

    return (STDON);
}

USHORT ProcessPassthru(NPUNITCB pUnitCB,PRP_GENIOCTL pRP,BOOL Direction)
{

  PCTRLSTRING pCtrl= (PCTRLSTRING)(pRP->ParmPacket);
  PVOID pData = (PVOID)(pRP->DataPacket);
  PBYTE pCommand= pCtrl->scsi_command;
  ULONG ulCommandLength = pCtrl->scsi_command_length;
  ULONG ulDataSize= pCtrl->datasize;
  ULONG ulSenseLength = pCtrl->sense_length;
  ULONG physData;
  BYTE  n;
  USHORT rc;
  NPIORB_CDB  pIORB;
  PCMDSTRUCT pCDBSpace;
  BYTE near * pSenseData;


  // clear response sense length
  pCtrl->sense_length=0;

  switch(ulCommandLength)
    {
    case 6:
    case 10:
    case 12:
      break;
    default:
      return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
     break;
    } /* endswitch */

  if(ulDataSize)
    {
    if( rc = DevHelp_VerifyAccess ((SEL) SELECTOROF(pData),
                                   (USHORT)ulDataSize,
                                   (USHORT)OFFSETOF(pData),
                                    VERIFY_READWRITE))
      {
      return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
      } /* endif */
    if(DevHelp_VirtToPhys(pData, (PULONG) &physData))
      {
      return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
      } /* endif */
    } /* endif */
  else
    {
    physData=-1l;
    } /* endelse */

  BuildIORB_PassthruCDB (pUnitCB, ulDataSize, physData, (NPIORB_CDB far *)&pIORB);

  pCDBSpace = (PCMDSTRUCT) &(pIORB->CDB);
  for(n=0; n<ulCommandLength; n++ )
    {
    pCDBSpace->commandArray[n]=pCtrl->scsi_command[n];
    } /* endfor */

  pIORB->apt.ControllerCmdLen = ulCommandLength;

  pIORB->apt.Flags = Direction? PT_DIRECTION_IN:0;

  rc=SubmitIORB_Wait(pUnitCB,pIORB);

  ulSenseLength = min(ulSenseLength, sizeof(SCSI_REQSENSE_DATA));
  if(ulSenseLength)
    {
    if(pIORB->status_block.SenseData->SenseKey)
      {
      if(DevHelp_VerifyAccess ((SEL) SELECTOROF(pCtrl->sense_data_area),
                                     (USHORT)ulSenseLength,
                                     (USHORT)OFFSETOF(pCtrl->sense_data_area),
                                      VERIFY_READWRITE ))
        {
        return ( STDON + STERR + ERROR_I24_INVALID_PARAMETER );
        } /* endif */

      pSenseData = (BYTE near *) &pIORB->sense_data;
      for(n=0; n<ulSenseLength;n++ )
        {
        pCtrl->sense_data_area[n] = pSenseData[n];
        } /* endfor */
      } /* endif */
    } /* endif */

  FreeIORB(pUnitCB,pIORB);

  return rc;
}

USHORT NEAR ReadCtrl( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)
{
    return(ProcessPassthru(pUnitCB, pRP,TRUE));
}

USHORT NEAR WriteCtrl( PRP_GENIOCTL pRP, NPUNITCB     pUnitCB)
{
    return(ProcessPassthru(pUnitCB, pRP,FALSE));
}
