/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = CDSTRAT1.C
 *
 * DESCRIPTIVE NAME = Strategy 1 interface for OS/2 CDROM Device Manager
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS   Provides validation and routing of Strategy 1 requests
 *             received from the OS/2 Kernel.
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#include "cdh.h"

USHORT NonSCSI_GetLastSessionAddr (NPUNITCB, ULONG FAR *);


USHORT (near *Strat1Near[])() =
{                     /*--------------------------------------*/
   CD_DriveInit,      /* 0x00  initialize                     */
   CD_MediaCheck,     /* 0x01  check the media                */
   CD_BuildBPB,       /* 0x02  build BPB                      */
   CmdErr,            /* 0x03  reserved                       */
   CD_Read,           /* 0x04  read                           */
   CmdErr,            /* 0x05  non-destructive read           */
   CmdErr,            /* 0x06  input status                   */
   CmdErr,            /* 0x07  input flush                    */
   WriteErr,          /* 0x08  write                          */
   WriteVErr,         /* 0x09  write with verify              */
   CmdErr,            /* 0x0A  get output status              */
   CmdErr,            /* 0x0B  flush output                   */
   CmdErr,            /* 0x0C  reserved                       */
   StatusComplete,    /* 0x0D  open                           */
   StatusComplete,    /* 0x0E  close                          */
   RemovableMedia,    /* 0x0F  removable media                */
   DriveGenIOCTL,     /* 0x10  generic IOCTL                  */
   ResetMedia,        /* 0x11  reset uncertain media          */
   GetLogDriveMap,    /* 0x12  get Logical Drive Map          */
   SetLogDriveMap,    /* 0x13  set Logical Drive Map          */
   CmdErr,            /* 0x14  de-Install this device         */
   CmdErr,            /* 0x15  reserved                       */
   PartFixedDisks,    /* 0x16  get number of partitions       */
   GetUnitMap,        /* 0x17  get unit map                   */
   CD_Read,           /* 0x18  no caching read                */
   WriteErr,          /* 0x19  no caching write               */
   WriteVErr,         /* 0x1A  no caching write/verify        */
   CD_DriveInit,      /* 0x1B  initialize                     */
   StatusComplete,    /* 0x1C  prepare for shutdown           */
   CmdErr,            /* 0x1D  Get Driver Capabilities        */
};                    /*--------------------------------------*/


/****************************************************************************
 *
 * FUNCTION NAME = CD_Strat1
 *
 * DESCRIPTION   = OS2CDROM strategy 1 routine
 *
 * INPUT         = ES:BX     - pointer to Request Packet
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

void near CD_Strat1(pRPH)

PRPH pRPH;
{
  NPUNITCB      pUnitCB;
  USHORT        Cmd, Status;


  pRPH->Status = 0;
  pRPH->Flags = 0;

  Cmd = pRPH->Cmd;

  /*
  ** Filter out invalid requests
  */
/*
**if (DDFlags & DDF_NO_MEDIA)
**{
**    Status = STDON + STERR + ERROR_I24_BAD_UNIT;
**    goto ExitDiskDD;
**}
*/
  if (Cmd > MAX_DISKDD_CMD)
  {
     Status = STDON + STERR + ERROR_I24_BAD_COMMAND;
     goto  ExitDiskDD;
  }

  if ( (Get_UnitCB_Addr(pRPH->Unit, (NPUNITCB FAR *) &pUnitCB) != NO_ERROR) &&
                     (Cmd != CMDInitBase) &&
                     (Cmd != CMDInit) &&
                     (Cmd != CMDPartfixeddisks) )
  {
      Status = STDON + STERR + ERROR_I24_BAD_UNIT;
      goto ExitDiskDD;
  }
/*
**if ( (Cmd != CMDInitBase) && IsTraceOn() )
**      Trace(TRACE_STRAT1 | TRACE_ENTRY, (PBYTE) pRPH, pUnitCB);
*/
  /*
  ** Call Worker Routine
  */

  Status = (*Strat1Near[Cmd])(pRPH, pUnitCB);

  /*
  ** Finish up by setting the Status word in the Request Packet Header
  */

ExitDiskDD:  ;

  DISABLE;

  pRPH->Status = Status;                  /* Save status in Request Packet */

  ENABLE;
/*
**if ( (TraceFlags != 0) && (pRPH->Status & STDON) && (Cmd != CMDInitBase) )
**   Trace(TRACE_STRAT1 | TRACE_EXIT, (PBYTE) pRPH, pVolCB);
*/
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_MediaCheck
 *
 * DESCRIPTION   = Check the Media    (Command = 0x01)
 *
 *       Checks to see if the media in the drive has changed.
 *
 *       USHORT CD_MediaCheck   (PRP_MEDIACHECK pRP, NPUNITCB pUnitCB)
 *
 *       EFFECTS:  The Return Code in the Request Packet is set to one
 *                 of the following:
 *
 *                     -1 = Media has been changed
 *                      0 = Unsure if media has been changed
 *                      1 = Media unchanged
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR CD_MediaCheck(pRP, pUnitCB)

PRP_MEDIACHECK    pRP;
NPUNITCB          pUnitCB;
{
   USHORT rc;
   USHORT cTries;                                                    /*V@91985*/
   NPIORB pIORB;

   if (pUnitCB->Flags & UCF_UNCERTAIN_MEDIA)
   {
      pRP->rc = -1;
      return(STDON);
   }

   BuildCDB_TestUnitReady (pUnitCB, (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   if ( ((NPIORB_CDB)pIORB)->sense_data.additional_sense_code !=     /*@V91985*/
                             ASC_MEDIUM_NOT_PRESENT )                /*@V91985*/
   {                                                                 /*@V91985*/
      cTries = 0;                                                    /*@V91985*/
      while ( (((NPIORB_CDB)pIORB)->sense_data.sense_key ==          /*@V91985*/
                                            SCSI_SK_NOTRDY )         /*@V91985*/
              && (cTries++ < MAX_NOT_READY_RETRIES ))                /*@V91985*/
      {                                                              /*@V91985*/
         FreeIORB (pUnitCB, (NPIORB_CDB) pIORB);                     /*@V91985*/
         DevHelp_ProcBlock( (ULONG) pIORB, NOT_READY_WAIT,           /*@V91985*/
                                    WAIT_IS_INTERRUPTABLE );         /*@V91985*/
         BuildCDB_TestUnitReady (pUnitCB,                            /*@V91985*/
                                 (NPIORB_CDB FAR *) &pIORB);         /*@V91985*/
         rc = SubmitIORB_Wait (pUnitCB, pIORB);                      /*@V91985*/
      }                                                              /*@V91985*/
   }                                                                 /*@V91985*/

   pRP->rc = ! (pUnitCB->Flags & UCF_UNCERTAIN_MEDIA);

   FreeIORB (pUnitCB, (NPIORB_CDB) pIORB);

   return(rc);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_BuildBPB
 *
 * DESCRIPTION   = Build the BPB      (Command = 0x02)
 *
 *       Builds the BPB.  This is requested when the media has changed
 *       of when the media type is uncertain.
 *
 *       USHORT BuildBPB   (PRP_BUILDBPB pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR CD_BuildBPB(pRP, pUnitCB)
PRP_BUILDBPB   pRP;
NPUNITCB       pUnitCB;
{
   USHORT rc;
   ULONG  volume_size;

   if ( (rc = GetVolumeSize (pUnitCB, (ULONG FAR *) &volume_size)) & STERR)
      return(rc);

   DefaultBPB.BigTotalSectors = volume_size;

   pRP->bpb = (PVOID) &(DefaultBPB);

   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Read
 *
 * DESCRIPTION   = Read Command  (Command 0x04)
 *
 *         This is the basic strategy-1 I/O read routine for the driver.
 *         The request is queued and sent to the adapter driver for
 *         processing.
 *
 *         USHORT CD_Read   (PRP_RWV pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Read (pRP, pUnitCB)

PRP_RWV   pRP;
NPUNITCB  pUnitCB;
{
   USHORT rc, disk_density;
   ULONG  EndSector;
   NPIORB_DMWORK pDMWork;
   BOOL   playing,
          MountDrive = FALSE;                                        /*@V91985*/
   PULONG pBuff;                                                     /*@V91985*/
   PULONG i;                                                         /*@V96674*/
   USHORT ModeFlag;                                                  /*@V91985*/
   NPIORB_CDB pIORB = 0, pModeIORB = 0;

   /*
   ** Check for valid input parms
   */
   if (pRP->NumSectors == 0)
      return (STDON);
/*
** if ((pRP->rba + pRP->NumSectors) > pUnitCB->TotalSectors)
**    return (STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);
*/
   /*
   ** Check for uncertain media
   */
   if (pUnitCB->Flags & UCF_UNCERTAIN_MEDIA)
   {
      pRP->NumSectors = 0;
      return(STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA);
   }

   /*
   ** Cant read when unit is playing so check play status
   */
   if (pUnitCB->DeviceInfo.playing)
   {
      rc = GetPlayStatus(pUnitCB, &playing);

      if ( (rc == STDON) && playing)
      {
         pRP->NumSectors = 0;
         return(STDON + STERR + ERROR_I24_DEVICE_IN_USE);
      }
   }

   /*
   ** If multisession photo CD mounted and the target read is one of the
   ** Volume Descriptor Sectors, then remap the RBA to the last session
   */
   if ( (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_MULTISESSION) &&
        (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_MULTISESSION_MOUNTED) &&
        (pRP->rba >= PRIMARY_VOL_DESCR_RBA) &&
        (pRP->rba <= pUnitCB->DeviceInfo.volume_descr_terminator) )
   {
      pRP->rba = pRP->rba + pUnitCB->DeviceInfo.last_session_addr;
   }

   /*                                                                  @V91985
   ** Determine if the File System is attempting to mount the Drive.   @V91985
   */                                                                /*@V91985*/
                                                                     /*@V91985*/
   if ( (pRP->rba == 0) &&                                           /*@V91985*/
        (pRP->NumSectors == 1) )                                     /*@V91985*/
   {                                                                 /*@V91985*/
      if ( DevHelp_PhysToVirt( (ULONG) pRP->XferAddr,                /*@V91985*/
                               (USHORT) 2048,                        /*@V96674*/
                               (PVOID)  &pBuff,                      /*@V91985*/
                               (PUSHORT) &ModeFlag   ) )             /*@V91985*/
      {                                                              /*@V91985*/
         _asm {int 3}                                                /*@V91985*/
      }                                                              /*@V91985*/
                                                                     /*@V91985*/
      if (*pBuff == 0x544f4f42l) /* Boot Signature */                /*@V91985*/
      {                                                              /*@V91985*/
         MountDrive = TRUE;                                          /*@V91985*/
      }                                                              /*@V91985*/
   }                                                                 /*@V91985*/

   /*
   ** Toshiba 3301 & 3401 is vendor unique since we must make sure the
   ** density code is set properly.
   */
   switch (pUnitCB->DeviceInfo.product_id_code)
   {
      case TOSHIBA_3301:
      case TOSHIBA_3401:
         rc = Tosh_Read_2048(pUnitCB, pRP->rba, pRP->NumSectors, pRP->XferAddr);
         break;


      default:

         if  ( (pUnitCB->DeviceInfo.current_block_size != 2048) &&
               (pUnitCB->DeviceInfo.interface_type != INTERFACE_ATAPI) )

         {
            rc = ChainModeSelectRead (pUnitCB, pRP->rba, pRP->NumSectors,
                                               pRP->XferAddr, 2048, 0);
         }
         else
         {
            rc = ReadSector (pUnitCB, pRP->rba, pRP->NumSectors,
                                                pRP->XferAddr, 2048);
         }
   }

   /*
   ** Sony 561 can't read 2048 byte Mode 2 Form 2 XA sectors with the
   ** Read 6 command. We must issue the Read CD-XA command.
   */
   if ( (pUnitCB->DeviceInfo.product_id_code == SONY_561) &&
        (rc == STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND) )
   {
        rc = Sony_Read_2048(pUnitCB, pRP->rba, pRP->NumSectors, pRP->XferAddr);
   }

   /*
   ** If reading sector 0 on an NEC SCSI drive and it's an XA disk then change
   ** the density code to XA, and reissue the read.
   */
   if ( (rc == STDON + STERR + ERROR_I24_READ_FAULT) &&
        (pUnitCB->DeviceInfo.vendor_id_code == NEC) &&
        (pUnitCB->DeviceInfo.interface_type == INTERFACE_SCSI) &&
        (pRP->rba == 0) )
   {
      rc = ChainModeSelectRead (pUnitCB, pRP->rba, pRP->NumSectors,
                                             pRP->XferAddr, 2048, 0x81);
   }

   if (rc & STERR)
      pRP->NumSectors = 0;

   if (rc == STDON + STERR + ERROR_I24_BAD_COMMAND)
      rc = STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND;

   /*
   ** If we are mounting the volume, then check for multi-session disk
   */
   if ( (rc == STDON) &&
        (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_MULTISESSION) &&
        (pRP->rba == 0) && (pRP->NumSectors == 1) )
   {
      Check_MultiSession_Mounted(pUnitCB, pRP->XferAddr);
   }

   /*                                                                  @V91985
   ** If the File System is attempting to mount the drive, and there   @V91985
   ** was an error that was not a drive ready error, give the error    @V91985
   ** (if any) from a read of the volumne descriptor.                  @V91985
   ** Clear the buffer if the read was successful.                     @V91985
   */                                                                /*@V91985*/
                                                                     /*@V91985*/
   if (MountDrive &&     // Atempting to mount a disc                /*@V96674*/
      (rc != STDON) &&   // An error was encountered                 /*@V96674*/
      (rc != STDON + STERR + ERROR_I24_NOT_READY) &&                 /*@V91985*/
      (rc != STDON + STERR + ERROR_I24_DISK_CHANGE) &&               /*@V91985*/
      (rc != STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA) )            /*@V91985*/
      {                                                              /*@V91985*/
         if ((rc = ReadSector(        pUnitCB,                       /*@V96674*/
                              (ULONG) PRIMARY_VOL_DESCR_RBA,         /*@V96674*/
                                      1,                             /*@V96674*/
                                      pRP->XferAddr,                 /*@V96674*/
                                      2048)) == STDON )              /*@V96674*/
         {  /* zero out buffer (512 ULONGS = 2048 bytes) */          /*@V96674*/
            for (i=pBuff; i<pBuff+512; i++ )                         /*@V96674*/
            {                                                        /*@V96674*/
               *i = 0l;                                              /*@V96674*/
            }                                                        /*@V96674*/
         }                                                           /*@V96674*/
      }                                                              /*@V91985*/

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = RemovableMedia
 *
 * DESCRIPTION   = Check for Removable Media    (Command = 0x0F)
 *
 *        USHORT RemovableMedia (PRPH pRPH, NPUNITCB pUnitCB)
 *
 *        EFFECTS:  The busy bit of the status word is set as follows:
 *
 *                       1 = Media is non-removable
 *                       0 = Media is removable
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT RemovableMedia(pRPH, pUnitCB)

PRPH     pRPH;
NPUNITCB pUnitCB;
{
   if (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
      return (STDON);
   else
      return (STDON + STBUI);
}



/****************************************************************************
 *
 * FUNCTION NAME = ResetMedia
 *
 * DESCRIPTION   = Reset Uncertain Media  (Command = 0x11)
 *
 *                 USHORT ResetMedia (PRPH pRPH, NPUNITCB pUnitCB)
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ResetMedia (pRPH, pUnitCB)

PRPH     pRPH;
NPUNITCB pUnitCB;
{
   USHORT rc;

   pUnitCB->Flags &= ~UCF_UNCERTAIN_MEDIA;

   /*
   ** Issue a Mode Select to set default density code to CD-ROM
   ** and default block size to 2048 bytes.
   */
   if (!( pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI ))
      rc = Submit_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2048);

   return (STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = GetLogDriveMap
 *
 * DESCRIPTION   = Get Logical Drive Mapping  (Command = 0x12)
 *
 *        Returns which logical drive is currently mapped onto a particular
 *        physical drive.  A zero is returned if only one logical drive is
 *        mapped to the physical drive.
 *
 *        USHORT GetLogDriveMap (PRPH pRPH, NPUNITCB pUnitCB)
 *
 *        EFFECTS: The logical drive is returned in the unit field
 *                 of the request packet header.  The logical drive
 *                 is actually LogDriveNum + 1, which represents the
 *                 drive letter, i.e. C: = 3.  A zero is returned
 *                 if only one logical drive is mapped to the physical drive.
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetLogDriveMap (pRPH, pUnitCB)

PRPH     pRPH;
NPUNITCB pUnitCB;
{

   pRPH->Unit = 0;                             /* ret 0 if one drive mapped */

   return (STDON);

}


/****************************************************************************
 *
 * FUNCTION NAME = SetLogDriveMap
 *
 * DESCRIPTION   = Set Logical Drive Mapping  (Command = 0x13)
 *
 *         Maps the specified logical drive onto the physical drive.
 *
 *         USHORT SetLogDriveMap (PRPH pRPH, NPVOLCB pVolCB)
 *
 *         EFFECTS: The logical drive is returned in the unit field
 *                  of the request packet header.  The logical drive
 *                  is actually LogDriveNum + 1, which represents the
 *                  drive letter, i.e. C: = 3.  A zero is returned
 *                  if only one logical drive is mapped to the physical drive.
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT SetLogDriveMap (pRPH, pUnitCB)

PRPH     pRPH;
NPUNITCB pUnitCB;

{
   pRPH->Unit = 0;                            /* only 1 drive can be mapped */

   return (STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = PartFixedDisks
 *
 * DESCRIPTION   = Get number of fixed disks  (Command = 0x16)
 *
 *        Returns the number of fixed disks supported by the driver.
 *
 *        USHORT PartFixedDisks (PRP_PARTFIXEDDISKS pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT PartFixedDisks (pRP, pUnitCB)

PRP_PARTFIXEDDISKS  pRP;
NPUNITCB            pUnitCB;
{
   pRP->NumFixedDisks = 0;

   return (STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = GetUnitMap
 *
 * DESCRIPTION   = Get logical units mapped to a physical unit (Command = 0x17)
 *
 *        This command is only supported for disk media.
 *
 *        USHORT GetUnitMap (PRP_GETUNITMAP pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetUnitMap (pRP, pUnitCB)

PRP_GETUNITMAP  pRP;
NPUNITCB        pUnitCB;
{

   pRP->UnitMap = 0;                                     /* No units mapped */

   return (STDON);
}



/*
** Near Entry Point to swappable IOCTL routine.
*/

/****************************************************************************
 *
 * FUNCTION NAME = DriveGenIOCTL
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT DriveGenIOCTL(pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;
{
  USHORT rc;

  rc = f_CD_DriveGenIOCTL (pRP, pUnitCB);

  rc |= STDON;

  return(rc);
}


/*
**   Packet Status return functions:
**
**   CmdErr, StatusError, StatusDevReady, StatusComplete
*/

/****************************************************************************
 *
 * FUNCTION NAME = CmdErr
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT near CmdErr (pRPH, pUnitCB)
PRPH     pRPH;
NPUNITCB pUnitCB;
{
   return (STERR + STDON + ERROR_I24_BAD_COMMAND);
}


/****************************************************************************
 *
 * FUNCTION NAME = WriteErr
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT near WriteErr (pRPH, pUnitCB)
PRPH     pRPH;
NPUNITCB pUnitCB;
{
   return (STERR + STDON + ERROR_I24_WRITE_FAULT);
}


/****************************************************************************
 *
 * FUNCTION NAME = WriteVErr
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT near WriteVErr (pRPH, pUnitCB)
PRPH     pRPH;
NPUNITCB pUnitCB;
{
   return (STERR + STDON + ERROR_I24_WRITE_FAULT);
}


/****************************************************************************
 *
 * FUNCTION NAME = StatusDevReady
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT StatusDevReady(pRPH, pUnitCB)
PRPH     pRPH;
NPUNITCB pUnitCB;
{
  return (STDON + STBUI);
}


/****************************************************************************
 *
 * FUNCTION NAME = StatusComplete
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT StatusComplete(pRPH, pUnitCB)
PRPH     pRPH;
NPUNITCB pUnitCB;
{
  return (STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = StatusError
 *
 * DESCRIPTION   =
 *
 * INPUT         = pRPH             - Request Packet Header
 *                 ErrorCode        - Error Code
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT StatusError( pRPH, ErrorCode )

PRPH    pRPH;
USHORT  ErrorCode;
{
  return (STDON + STERR);
}


/****************************************************************************
 *
 * FUNCTION NAME = ChainModeSelectRead
 *
 * DESCRIPTION   = Chain Mode Select and Read Commands
 *
 *         USHORT ChainModeSelectRead (NPUNITCB pUnitCB, ULONG LBA,
 *                                     USHORT transfer_count, ULONG ppDataBuff,
 *                                     USHORT block_length, USHORT density)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 LBA              - LBA
 *                 transfer_count   - count of sectors to transfer
 *                 ppDataBuff       - phys addr of data buffer
 *                 block_length     - block_length
 *                 density          - density code
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * NOTES:  This routine should not be called for an ATAPI device since
 *         ATAPI drives do not support Mode Selects for density codes
 *         and block lengths.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ChainModeSelectRead (pUnitCB, LBA, transfer_count, ppDataBuff,
                                          block_length, density)

NPUNITCB pUnitCB;
ULONG    LBA;
USHORT   transfer_count;
ULONG    ppDataBuff;
USHORT   block_length;
USHORT   density;

{
   USHORT rc;

   NPIORB_CDB pIORB = 0, pModeIORB = 0;
   NPIORB_DMWORK pDMWork;

   BuildCDB_ModeSelect (pUnitCB, density, block_length,
                                    (NPIORB_CDB FAR *) &pModeIORB);

   /*
   ** If transfer count < 255 sectors, issue Read (6) else issue Read (10)
   */
   if (transfer_count <= 255)
   {
      BuildCDB_Read_6  (pUnitCB, LBA, block_length,
                                  transfer_count, ppDataBuff,
                                  (NPIORB_CDB FAR *) &pIORB);
   }
   else
   {
      BuildCDB_Read_10 (pUnitCB, LBA, block_length,
                                 transfer_count, ppDataBuff,
                                 (NPIORB_CDB FAR *) &pIORB);
   }

   pDMWork = (NPIORB_DMWORK) &(pModeIORB->apt.iorbh.DMWorkSpace);
   pDMWork->pCoReqIORB = (NPIORB) pIORB;

   QueueIORB (pUnitCB, pModeIORB);

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   if ( !(pModeIORB->apt.iorbh.Status & IORB_ERROR) )
   {
      pUnitCB->DeviceInfo.current_density = density;
      pUnitCB->DeviceInfo.current_block_size = block_length;

   }

   FreeIORB (pUnitCB, pModeIORB);
   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = ReadSector
 *
 * DESCRIPTION   = Issue Read Command
 *
 *        USHORT ReadSector (NPUNITCB pUnitCB, ULONG LBA,
 *                           USHORT transfer_count, ULONG ppDataBuff,
 *                           USHORT block_length)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 LBA              - LBA
 *                 transfer_count   - count of sectors to transfer
 *                 ppDataBuff       - phys addr of data buffer
 *                 block_length     - block_length
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ReadSector (pUnitCB, LBA, transfer_count, ppDataBuff, block_length)

NPUNITCB pUnitCB;
ULONG    LBA;
USHORT   transfer_count;
ULONG    ppDataBuff;
USHORT   block_length;

{
   USHORT rc;
   NPIORB_CDB pIORB = 0;

   /*
   ** If transfer count < 255 sectors, issue Read (6) else issue Read (10)
   ** ATAPI drives only support Read 10.
   */
   if ( (transfer_count <= 255) &&
        (pUnitCB->DeviceInfo.interface_type != INTERFACE_ATAPI) )
   {
      BuildCDB_Read_6  (pUnitCB, LBA, block_length,
                                  transfer_count, ppDataBuff,
                                  (NPIORB_CDB FAR *) &pIORB);
   }
   else
   {
      BuildCDB_Read_10 (pUnitCB, LBA, block_length,
                                 transfer_count, ppDataBuff,
                                 (NPIORB_CDB FAR *) &pIORB);
   }

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = GetVolumeSize
 *
 * DESCRIPTION   = Get volume size
 *
 *       Return the volume size of the current CD
 *
 *       USHORT GetVolumeSize  (NPUNITCB pUnitCB, ULONG FAR * volume_size)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 volume_size      - Pointer to return volume size
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetVolumeSize (pUnitCB, volume_size)

NPUNITCB  pUnitCB;
ULONG FAR * volume_size;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct ReadCapacity_Data NEAR *pCDBData;
   union  ULONGB  ul_volume;


   BuildCDB_ReadCapacity(pUnitCB, (NPIORB_CDB FAR *) &pIORB);

   pCDBData = (struct ReadCapacity_Data NEAR *) pIORB->CDB_data;

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   if (rc == STDON)
   {
      ul_volume.ulbytes.byte_3  =  pCDBData->capacity_LBA.ulbytes.byte_0;
      ul_volume.ulbytes.byte_2  =  pCDBData->capacity_LBA.ulbytes.byte_1;
      ul_volume.ulbytes.byte_1  =  pCDBData->capacity_LBA.ulbytes.byte_2;
      ul_volume.ulbytes.byte_0  =  pCDBData->capacity_LBA.ulbytes.byte_3;

      *volume_size = ul_volume.dword;
   }
   else if (rc == STDON + STERR + ERROR_I24_INVALID_PARAMETER)
      ul_volume.dword = 270000L;


   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = ClearCheckCondition
 *
 * DESCRIPTION   = Clear check condition and return error code
 *
 *                 USHORT ClearCheckCondition  (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ClearCheckCondition (pUnitCB)

NPUNITCB pUnitCB;
{
   ULONG  volume_size;

   return(GetVolumeSize (pUnitCB, (ULONG FAR *) &volume_size) );
}


/****************************************************************************
 *
 * FUNCTION NAME = GetSectorMode
 *
 * DESCRIPTION   = Get mode for sector zero.
 *
 *       This routine returns the mode of sector zero by reading the header.
 *
 *       USHORT GetSectorMode  (NPUNITCB pUnitCB, USHORT FAR * mode)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 mode             - mode
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetSectorMode (pUnitCB, mode)

NPUNITCB pUnitCB;
USHORT   FAR *mode;

{
   USHORT rc;
   NPIORB_CDB pIORB;
   struct ReadHeader_Data NEAR *pCDBData;


   BuildCDB_ReadHeader(pUnitCB, 0, (NPIORB_CDB FAR *) &pIORB);

   pCDBData = (struct ReadHeader_Data NEAR *) pIORB->CDB_data;

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   if (rc == STDON)
   {
      *mode = pCDBData->cdrom_data_mode;
   }

   FreeIORB (pUnitCB, pIORB);
   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Check_MultiSession_Mounted
 *
 * DESCRIPTION   = Check if multisession mounted
 *
 *      This routine checks to see if a multisession photoCD disk is mounted.
 *      If it is, then the driver reads the volume descriptors until the
 *      volume descriptor terminator is found.  All subsequent reads from
 *      the Primary Volume Descriptor till the Volume Descriptor Terminator
 *      are mapped to the last session prior to the read.  This routine is
 *      only called if the multi_session_support bit is set in the UnitCB.
 *
 *      USHORT Check_MultiSession_Mounted  (NPUNITCB pUnitCB, ULONG physaddr)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 physaddr         - physical address of I/O buffer
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Check_MultiSession_Mounted (pUnitCB, physaddr)

NPUNITCB pUnitCB;
ULONG    physaddr;

{
   USHORT rc, n, mode_flag;
   ULONG session_addr;
   PBYTE pBuffer;

   BOOL term_found = FALSE;

   /*
   ** Initialize to not multisession disk
   */
   pUnitCB->DeviceInfo.Audio.capabilities &= ~DCAPS_MULTISESSION_MOUNTED;
   pUnitCB->DeviceInfo.last_session_addr = 0;
   pUnitCB->DeviceInfo.volume_descr_terminator = 0;

   switch (pUnitCB->DeviceInfo.product_id_code)
   {
      case TOSHIBA_3401:
         rc = Tosh_GetLastSessionAddr (pUnitCB, (ULONG FAR *) &session_addr);
         break;

      case SONY_561:
      case TEXEL_3024K:
      case PIONEER_604X:
      case HITACHI_6750:
      case LMS_215:
         rc = Sony_GetLastSessionAddr (pUnitCB, (ULONG FAR *) &session_addr);
         break;

      case CHINON_535:
         rc = Chinon_GetLastSessionAddr (pUnitCB, (ULONG FAR *) &session_addr);
         break;

      case NEC_84_1:
         rc = NEC_GetLastSessionAddr (pUnitCB, (ULONG FAR *) &session_addr);
         break;

      default:
         if (pUnitCB->DeviceInfo.interface_type == INTERFACE_PROPRIETARY)
            rc=NonSCSI_GetLastSessionAddr(pUnitCB, (ULONG FAR *) &session_addr);
         else if (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
            rc = Sony_GetLastSessionAddr (pUnitCB, (ULONG FAR *) &session_addr);
         else
            rc = STDON + STERR;
         break;
   }
   /*
   ** If no error, then multisession disk.  Read the volume descriptors
   ** till the terminator descriptor is found.
   */
   if (rc == STDON)
   {
      for (n = PRIMARY_VOL_DESCR_RBA; term_found != TRUE; n++)
      {
         if ((rc = ReadSector(pUnitCB, (ULONG) n, 1, physaddr, 2048)) != STDON)
            break;
         /*
         ** Check for the volume descriptor id of "CD001"
         */
         DevHelp_PhysToVirt (physaddr,
                            (USHORT) 2048,
                            (PBYTE FAR *) &pBuffer,
                            (USHORT FAR *) &mode_flag);


         if ( *(pBuffer+1) != 'C' || *(pBuffer+2) != 'D' ||
              *(pBuffer+3) != '0' || *(pBuffer+4) != '0' ||
              *(pBuffer+5) != '1' )
         {
            rc = STDON + STERR;
            break;
         }
         /*
         ** Check for terminator volume descriptor
         */
         if ( (UCHAR) *pBuffer == VOL_DESCR_TERMINATOR)
         {
           pUnitCB->DeviceInfo.Audio.capabilities |= DCAPS_MULTISESSION_MOUNTED;
           pUnitCB->DeviceInfo.last_session_addr = session_addr;
           pUnitCB->DeviceInfo.volume_descr_terminator = n;
           term_found = TRUE;
         }
      }
      /*
      ** Read back original sector 0
      */
      ReadSector (pUnitCB, (ULONG) 0, 1, physaddr, 2048);
   }
}


/****************************************************************************
 *
 * FUNCTION NAME = NonSCSI_GetLastSessionAddr
 *
 * DESCRIPTION   = Get address of last session on multisession disk
 *
 *  This routine returns the address of the last session of a multisession
 *  photo CD disk.  This routine should only be called is the drive is
 *  a non SCSI CD-ROM.
 *
 *  USHORT NonSCSI_GetLastSessionAddr(NPUNITCB pUnitCB, ULONG FAR *session_addr)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 session_addr     - Pointer to returned session addr
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 *                                    if the STERR bit is on, then the disk
 *                                      is not a multisession disk and the
 *                                      session_addr field is not valid
 *
 *                                    if the STERR bit is NOT set, then the
 *                                      disk is a multi-session disk and the
 *                                      session addr field is valid
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NonSCSI_GetLastSessionAddr (pUnitCB, session_addr)

NPUNITCB pUnitCB;
ULONG    FAR *session_addr;

{
   USHORT rc;
   NPIORB_CDB pIORB;
   union ADD_ReadDiskInfo_Data NEAR *pCDBData;
   union  AddressType last_session;


   ADD_BuildCDB_ReadDiskInfo (pUnitCB, TYPE_LASTSESSION_INFO,
                                        (NPIORB_CDB FAR *) &pIORB);

   pCDBData = (union ADD_ReadDiskInfo_Data NEAR *) pIORB->CDB_data;

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   if (rc == STDON)
   {
      /*
      ** If zero returned in data fields, then not multisession
      */
      if ( (pCDBData->last_session.amin == 0) &&
           (pCDBData->last_session.asec == 0) &&
           (pCDBData->last_session.aframe == 0) )
      {
         rc = STDON + STERR;
      }
      else
      {
         last_session.ul_redbook.min = pCDBData->last_session.amin;
         last_session.ul_redbook.sec = pCDBData->last_session.asec;
         last_session.ul_redbook.frame = pCDBData->last_session.aframe;
         last_session.ul_redbook.zero = 0;

         last_session.dword = RedBookToHSG (last_session.dword);

         *session_addr = last_session.dword;
      }

   }
   FreeIORB (pUnitCB, pIORB);
   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = GetPlayStatus
 *
 * DESCRIPTION   = Get play status
 *
 *                 USHORT GetPlayStatus  (NPUNITCB pUnitCB, BOOL FAR * playing)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 playing          - returned play status
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT  GetPlayStatus (pUnitCB, playing)

NPUNITCB   pUnitCB;
BOOL FAR *playing;
{
   USHORT rc;
   NPIORB_CDB pIORB;
   struct SubChannel_Position NEAR *pCDBData;


   BuildCDB_ReadSubChannel(pUnitCB, RSC_CURRENT_POSITION,
                                           (NPIORB_CDB FAR *) &pIORB);

   pCDBData = (struct SubChannel_Position NEAR *) pIORB->CDB_data;

   *playing = FALSE;

   if ( (rc = SubmitIORB_Wait(pUnitCB, pIORB)) == STDON)
   {
      if (pCDBData->sub_channel_hdr.audio_status == AS_PLAY_IN_PROGRESS)
         *playing = TRUE;

      pUnitCB->DeviceInfo.playing = *playing;
   }
   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Submit_ModeSelect
 *
 * DESCRIPTION   = Submit a Mode Select command
 *
 *                 Submit_ModeSelect (NPUNITCB pUnitCB, USHORT density_code,
 *                                                      USHORT block_length)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 density_code    - density code
 *                 block_length    - block_length
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = USHORT          - packet status
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Submit_ModeSelect (pUnitCB, density_code, block_length)

NPUNITCB   pUnitCB;
USHORT     density_code;
USHORT     block_length;
{

   USHORT rc;
   NPIORB_CDB pIORB;

   BuildCDB_ModeSelect (pUnitCB, density_code, block_length,
                                                (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc == STDON)
   {
      pUnitCB->DeviceInfo.current_density    = density_code;
      pUnitCB->DeviceInfo.current_block_size = block_length;
   }

   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Get_UnitCB_Addr
 *
 * DESCRIPTION   = Get UnitCB address
 *
 *       Return the address of the UnitCB for the specified unit.
 *
 *       USHORT Get_UnitCB_Addr  (UCHAR LogDriveNum, NPUNITCB FAR *pUnitCB)
 *
 * INPUT         = unit             - logical drive number
 *                 pUnitCB          - returned pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Get_UnitCB_Addr (LogDriveNum, pUnitCB)

UCHAR LogDriveNum;
NPUNITCB FAR *pUnitCB;

{
   NPUNITCB pUnitCBx;
   USHORT   found = FALSE;

   pUnitCBx = UnitCB_Head;

   while (pUnitCBx != NULL && found == FALSE)
   {
      if ( (UCHAR) pUnitCBx->LogDriveNum == LogDriveNum )
         found = TRUE;
      else
         pUnitCBx = pUnitCBx->pNextUnitCB;
   }

   if (found == TRUE)
   {
      *pUnitCB = pUnitCBx;
      return(NO_ERROR);
   }
   else
      return(ERROR);
}


/****************************************************************************
 *
 * FUNCTION NAME = Strncmp
 *
 * DESCRIPTION   = string compare
 *
 *                 Strncmp (NPBYTE string1, NPBYTE string2, USHORT length)
 *
 *                 This is like the standard C function, except it is boolean
 *
 * INPUT         = string1         - string 1
 *                 string2         - string 2
 *                 length          - length of string
 *
 * OUTPUT        = BOOLEAN, 1 if equal, 0 if not equal
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL Strncmp ( string1, string2, length )

UCHAR   *string1, *string2;
USHORT  length;

{
    BOOL    equal = TRUE;

    while ( length-- > 0 )
    {
        if ( *string1++ != *string2++ )
        {
            equal = FALSE;
            break;
        }
    }
    return ( equal );
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Strat1b
 *
 * DESCRIPTION   = VOID CD_strat1b  ()
 *
 * INPUT         = ES:BX    - Request Packet
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID NEAR CD_Strat1b()
{
  PRP_GENIOCTL  pRP;
  USHORT        Cmd, Status;
  PRPINITOUT    pRPO;

  _asm
  {
     push es
     push bx
     mov word ptr pRP[0], bx
     mov word ptr pRP[2], es
  }

  pRPO = (PRPINITOUT) pRP;                  /* Output for Init RP           */
  Status = 0;

  Cmd = pRP->rph.Cmd;

  if (Cmd == CMDInitBase || Cmd == CMDInit)
  {
     pRPO->DataEnd = DataBreakAddress;
     pRPO->CodeEnd = CodeBreakAddress;
     if (CodeBreakAddress == 0)
        Status = STDON + STERR + ERROR_I24_QUIET_INIT_FAIL;
  }
  else if ( (Cmd == CMDGenIOCTL) && (pRP->Category == IOC_CDROM_2) &&
            (pRP->Function == IOCD_RETURN_DRIVE_LETTER) )
  {

/*   Status = VerifyParameters(pRP, 0, 4, ParamLockHandle, DataLockHandle);*/

     if (Status == 0)
     {
        ((struct DriveLetter_Data FAR *)pRP->DataPacket)->drive_count =
                                                           NumCDROMDrives;

        ((struct DriveLetter_Data FAR *)pRP->DataPacket)->first_drive_number =
                                                           FirstDriveNumber;
     }
  }
  else
     Status = STDON + STERR + ERROR_I24_BAD_COMMAND;

  DISABLE;

  pRP->rph.Status = Status | STDON ;         /*Save status in Request Packet */

  ENABLE;

  _asm
  {
     pop bx
     pop es
     LEAVE
     retf
  }
}

