/*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 = CDIOC80.C
 *
 * DESCRIPTIVE NAME = IOCTL handling routines for OS/2 CD-ROM Device Mgr
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "cdh.h"
#include "dskioctl.h"

USHORT GetRawBlockSize (NPUNITCB);


/****************************************************************************
 *
 * FUNCTION NAME = CD_ResetDrive
 *
 * DESCRIPTION   = Issue a reset drive command to the device.
 *
 *                 USHORT CD_ResetDrive (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ResetDrive (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT      rc, i;
   BOOL        playing;
   NPIORB_CDB  pIORB;

   struct    Status *audio_status;
   struct    Audio  *audio;

   /*
   ** If device is playing, return DEVICE_IN_USE error
   */
   rc = GetPlayStatus (pUnitCB, &playing);

   if ( (rc == STDON) && playing)
      return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);

   /*
   ** First, abort any oustanding commands on the device
   */
   BuildIORB_DeviceControl (pUnitCB, IOCM_ABORT, (NPIORB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   /*
   ** Reset the device
   */
/*
** BuildIORB_DeviceControl (pUnitCB, IOCM_RESET, (NPIORB FAR *) &pIORB);
**
** rc = SubmitIORB_Wait (pUnitCB, pIORB);
**
** FreeIORB (pUnitCB, pIORB);
*/
   /*
   ** Recover from the reset. First clear the check condition from the
   ** reset.  Then delay long enough to recover from the not ready to
   ** ready transition.
   */
   ClearCheckCondition(pUnitCB);

   for (i = 1; i <= 10; i++)
   {
       DevHelp_ProcBlock( (ULONG) ppDataSeg, (ULONG) 1000, 0);

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

       rc = SubmitIORB_Wait (pUnitCB, pIORB);

       FreeIORB (pUnitCB, pIORB);

       if (rc == STDON)
          break;
   }

   /*
   ** Issue SCSI Pause Command. Ignore the RC
   */
/*
** if (pUnitCB->Flags & UCF_AUDIO_SUPPORTED)
** {
**    BuildCDB_PauseResume (pUnitCB, CDBF_PAUSE, (NPIORB_CDB FAR *) &pIORB);
**
**    rc = SubmitIORB_Wait (pUnitCB, pIORB);
**
**    FreeIORB (pUnitCB, pIORB);
** }
*/
   /*
   ** Update Audio status fields in UnitCB
   */
   audio = &pUnitCB->DeviceInfo.Audio;
   audio_status = &audio->status;

   pUnitCB->DeviceInfo.Audio.capabilities &= ~DCAPS_DOOR_LOCKED;
   pUnitCB->Flags &= ~UCF_UNCERTAIN_MEDIA;

   audio_status->paused = FALSE;
   audio_status->last_start_location.min   = 0;
   audio_status->last_start_location.sec   = 0;
   audio_status->last_start_location.frame = 0;
   audio_status->last_end_location.min     = 0;
   audio_status->last_end_location.sec     = 0;
   audio_status->last_end_location.frame   = 0;

   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_EjectDisk
 *
 * DESCRIPTION   = Issue an eject disk command to the device.
 *
 *                 USHORT CD_EjectDisk (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_EjectDisk (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT     rc;
   BOOL       playing;
   NPIORB_CDB pIORB;

   /*
   ** If device is playing, return DEVICE_IN_USE error
   */
   rc = GetPlayStatus (pUnitCB, &playing);

   if ( (rc == STDON) && playing)
      return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);

   /*
   ** Issue SCSI Start-Stop Unit command to eject the media
   */
   BuildCDB_StartStopUnit (pUnitCB, CDBF_EJECT, (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc == STDON + STERR + ERROR_I24_NOT_READY)
      rc = STDON;

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_CloseTray
 *
 * DESCRIPTION   = Issue a close tray command to the device.
 *
 *                 USHORT CD_CloseTray (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_CloseTray (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT     rc;
   BOOL       playing;
   NPIORB_CDB pIORB;

   /*
   ** If device is playing, return DEVICE_IN_USE error
   */
   rc = GetPlayStatus (pUnitCB, &playing);

   if ( (rc == STDON) && playing)
      return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);

   /*
   ** Issue SCSI Start-Stop Unit command to close the tray
   */
   BuildCDB_StartStopUnit (pUnitCB, CDBF_CLOSE_TRAY, (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc == STDON + STERR + ERROR_I24_NOT_READY)
      rc = STDON;

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_LockUnLock
 *
 * DESCRIPTION   = Issue a lock or unlock drive door command to the device.
 *
 *                 USHORT CD_LockUnLock (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_LockUnLock (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   NPIORB_CDB pIORB;
   USHORT     rc, lock_flag;
   struct     Audio *audio;


   audio = &pUnitCB->DeviceInfo.Audio;

   if (pRP->Category == 8)
      lock_flag = ((PDDI_DsktRemovMediaCtl_param)pRP->ParmPacket)->Command;
   else
      lock_flag = ((struct LockUnlock FAR *)pRP->ParmPacket)->lock_flag;

   if (lock_flag > CDBF_LOCK_DOOR)
      return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   /*
   ** Issue SCSI PreventAllowRemoval command to lock/unlock the media
   */
   BuildCDB_PreventAllowRemoval (pUnitCB, lock_flag, (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc == STDON)
   {
      if (lock_flag)
         audio->capabilities |= DCAPS_DOOR_LOCKED;
      else
         audio->capabilities &= ~DCAPS_DOOR_LOCKED;

   }

   return(rc);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Seek
 *
 * DESCRIPTION   = Issue a seek command to the device.
 *
 *                 USHORT CD_Seek (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Seek(pRP,pUnitCB)
PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   BOOL   playing;
   NPIORB_CDB pIORB;
   struct Seek FAR *pParmPacket;
   union  ULONGB ul_LBA;

   pParmPacket = (struct Seek FAR *) pRP->ParmPacket;

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

   ul_LBA.dword = pParmPacket->start_sector;

   switch(pParmPacket->address_mode)
   {
      case CDROM_HSG_MODE:
         break;
      case CDROM_REDBOOK_MODE:
         ul_LBA.dword = RedBookToHSG (ul_LBA.dword);
         break;
      default:
         return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);
   }

   rc = GetPlayStatus (pUnitCB, &playing);

   if ( (rc == STDON) && playing)
      return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);

   /*
   ** Issue the seek.  If there's a seek error and the density code is not
   ** for CD-DA, the issue the Mode Select to change the density to CD-DA
   ** and retry the seek operation.
   */
   if (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
      BuildCDB_Seek_10(pUnitCB, (ULONG)ul_LBA.dword, (NPIORB_CDB FAR *)&pIORB);
   else
      BuildCDB_Seek_6(pUnitCB, (ULONG)ul_LBA.dword, (NPIORB_CDB FAR *)&pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if ( (rc == STDON + STERR + ERROR_I24_SEEK) &&
        ( (pUnitCB->DeviceInfo.product_id_code == TOSHIBA_3301) ||
          (pUnitCB->DeviceInfo.product_id_code == TOSHIBA_3401) ) &&
        (pUnitCB->DeviceInfo.current_density != CD_DENSITY_CDDA) )
   {

      BuildCDB_ModeSelect (pUnitCB, CD_DENSITY_CDDA, 2352,
                                                  (NPIORB_CDB FAR *) &pIORB);

      rc = SubmitIORB_Wait (pUnitCB, pIORB);

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

      FreeIORB (pUnitCB, pIORB);

      if (rc == STDON)
      {
         BuildCDB_Seek_6 (pUnitCB,(ULONG) ul_LBA.dword,
                                                  (NPIORB_CDB FAR *) &pIORB);
         rc = SubmitIORB_Wait (pUnitCB, pIORB);

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

/****************************************************************************
 *
 * FUNCTION NAME = CD_WriteLong
 *
 * DESCRIPTION   = Issue a write long command to the device.
 *
 *                 USHORT CD_WriteLong (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_WriteLong (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   return (STDON + STERR + ERROR_I24_WRITE_FAULT);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_WriteVLong
 *
 * DESCRIPTION   = Issue a write verify long command to the device.
 *
 *                 USHORT CD_WriteVLong (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_WriteVLong (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   return (STDON + STERR + ERROR_I24_WRITE_FAULT);
}

/****************************************************************************
 *
 * FUNCTION NAME = CD_DeviceStatus
 *
 * DESCRIPTION   = Return device status.
 *
 *                 USHORT CD_DeviceStatus (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_DeviceStatus (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   ULONG  device_status;
   BOOL   playing;

   device_status = DSF_DEFAULT;

   if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_DOOR_LOCKED)
      device_status &= ~DSF_DOOR_UNLOCKED;

   if ( (rc = GetPlayStatus (pUnitCB, &playing)) & STERR)
      playing = 0;

   if (playing)
      device_status |= DSF_PLAYING;

   rc = ClearCheckCondition(pUnitCB);

   if (rc == STDON + STERR + ERROR_I24_NOT_READY)
      device_status |= DSF_DOOR_OPEN + DSF_NO_DISK_PRESENT;

   if (pUnitCB->DeviceInfo.product_id_code != TOSHIBA_3201)
      device_status |= DSF_LONG_SECTORS;

   if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_XA)
      device_status |= DSF_XA_SUPPORT;

   if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_CDDA)
      device_status |= DSF_CDDA_SUPPORT;

   ((struct DeviceStatus_Data FAR *)pRP->DataPacket)->device_status =
                                                 (ULONG) device_status;

   return(STDON);
}

/****************************************************************************
 *
 * FUNCTION NAME = CD_Identify
 *
 * DESCRIPTION   = Identify device.
 *
 *                 USHORT CD_Identify (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Identify (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   struct IdentifyCDROMdriver FAR      *pParmPkt;
   struct IdentifyCDROMdriver_Data FAR *pDataPkt;
   union ULONGB parameter;
   USHORT rc = 0;


   pParmPkt = (struct IdentifyCDROMdriver FAR *) pRP->ParmPacket;
   pDataPkt = (struct IdentifyCDROMdriver_Data FAR *) pRP->DataPacket;


/*
** rc = VerifyAccess (pParmPkt,
**                    sizeof(struct IdentifyCDROMdriver),
**                    READ_ACCESS);
** if (rc & STERR)
**    return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);
**
*/

   parameter.dword = pParmPkt->ID_code;
   if (parameter.ulbwords.word_0 != CD)
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   if (parameter.ulbytes.byte_2 > '9' || parameter.ulbytes.byte_2 < '0')
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   if (parameter.ulbytes.byte_3 > '9' || parameter.ulbytes.byte_3 < '0')
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);


/*
**
** rc = VerifyAccess (pDataPkt,
**                    sizeof(struct IdentifyCDROMdriver_data),
**                    READ_WRITE_ACCESS);
*/


   if (rc & STERR)
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);


   pDataPkt->ID_code = CD01;

   return(STDON);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_ReturnSectorSize
 *
 * DESCRIPTION   = Return the sector size of the volume.
 *
 *                 USHORT CD_ReturnSectorSize (PRP_GENIOCTL pRP,
 *                                                     NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ReturnSectorSize (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT   rc;

   if ( (rc = ClearCheckCondition(pUnitCB)) ==
                              STDON + STERR + ERROR_I24_NOT_DOS_DISK)
   {
      return(rc);
   }

   ((struct ReturnSectorSize_Data FAR *)pRP->DataPacket)->sector_size =
                                                          CDROM_SECTOR_SIZE;
   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_HeadLocation
 *
 * DESCRIPTION   = Return the current head location.
 *
 *                 USHORT CD_HeadLocation(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_HeadLocation (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   NPIORB_CDB pIORB;
   USHORT     rc, address_mode;

   struct SubChannel_Position NEAR *pCDBData;
   struct LocationOfHead_Data FAR *pDataPkt;
   union  AddressType location;

   pDataPkt = (struct LocationOfHead_Data FAR *) pRP->DataPacket;


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

   address_mode = ((struct LocationOfHead FAR *)pRP->ParmPacket)->address_mode;

   if (address_mode > CDROM_REDBOOK_MODE)
      return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   if ( (rc = ClearCheckCondition (pUnitCB) ) & STERR)
      return(rc);

   /*
   ** Issue SCSI Read SubChannel - Current Position Command
   */
   BuildCDB_ReadSubChannel(pUnitCB, RSC_CURRENT_POSITION,
                                    (NPIORB_CDB FAR *) &pIORB);

   if ( (rc = SubmitIORB_Wait(pUnitCB, pIORB, (PBYTE) pRP)) == STDON)
   {
      /*
      ** Return the current head location from the CDB data area
      */
      pCDBData = (struct SubChannel_Position NEAR *) pIORB->CDB_data;

      if (pUnitCB->DeviceInfo.product_id_code == NEC_260_17B)
      {
         location.ul_redbook.min
                             = BCDtoBinary(pCDBData->abs_address.redbook.min);
         location.ul_redbook.sec
                             = BCDtoBinary(pCDBData->abs_address.redbook.sec);
         location.ul_redbook.frame
                             = BCDtoBinary(pCDBData->abs_address.redbook.frame);
      }
      else
      {
         location.ul_redbook.min   = pCDBData->abs_address.redbook.min;
         location.ul_redbook.sec   = pCDBData->abs_address.redbook.sec;
         location.ul_redbook.frame = pCDBData->abs_address.redbook.frame;
      }

      location.ul_redbook.zero  = 0;

      if (address_mode == CDROM_HSG_MODE)
         location.dword = RedBookToHSG (location.dword);
   }

   pDataPkt->location_of_head = location.dword;

   FreeIORB(pUnitCB, pIORB);

   return(rc);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_ReadPrefetch
 *
 * DESCRIPTION   = Read prefetch.
 *
 *                 USHORT CD_ReadPrefetch(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ReadPrefetch (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc, transfer_count;
   struct ReadPrefetch FAR *pParmPacketR;
   struct Seek FAR *pParmPacketS;

   pParmPacketR = (struct ReadPrefetch FAR *) pRP->ParmPacket;
   pParmPacketS = (struct Seek FAR *) pRP->ParmPacket;

   /*
   ** Convert the prefech to a seek operation
   */
   transfer_count = pParmPacketR->transfer_count;
   pParmPacketS->start_sector = pParmPacketR->start_sector;

   rc = CD_Seek (pRP, pUnitCB);

   /*
   ** Put the Read Prefetch parameter packet back to original
   */
   pParmPacketR->start_sector = pParmPacketS->start_sector;
   pParmPacketR->transfer_count = transfer_count;

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_ReadLong
 *
 * DESCRIPTION   = Read long.
 *
 *                 USHORT CD_ReadLong(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ReadLong (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   BOOL   playing;
   USHORT rc, raw_block_size;
   struct ReadLong FAR *pParmPacket;
   union  ULONGB ul_LBA;
   ULONG  ppDataBuff;
   USHORT transfer_count;


   pParmPacket = (struct ReadLong FAR *) pRP->ParmPacket;

   ul_LBA.dword = pParmPacket->start_sector;

   switch(pParmPacket->address_mode)
   {
      case CDROM_HSG_MODE:
         break;
      case CDROM_REDBOOK_MODE:
         ul_LBA.dword = RedBookToHSG (ul_LBA.dword);
         break;
      default:
         return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);
   }

   /*
   ** Check for uncertain media.  Also, cant read while play in progress
   */
   if (pUnitCB->Flags & UCF_UNCERTAIN_MEDIA)
      return (STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA);

   if ( pUnitCB->DeviceInfo.playing )
   {
       rc = GetPlayStatus (pUnitCB, &playing);

       if (rc == STDON && playing)
           return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);
   }

   rc = DevHelp_VirtToPhys (pRP->DataPacket, (PULONG) &ppDataBuff);
   transfer_count =  pParmPacket->transfer_count;

   /*
   ** Determine what the device's raw block size is.  Some drives
   ** support reading all 2352 bytes, others only support 2340.  The call
   ** is only done the first time a read long is issued.
   */
   if (pUnitCB->DeviceInfo.raw_block_size == 0)
   {
       if ( (rc = GetRawBlockSize(pUnitCB)) & STERR)
          return(rc);
   }

   raw_block_size = pUnitCB->DeviceInfo.raw_block_size;


   switch (pUnitCB->DeviceInfo.product_id_code)
   {
      case TOSHIBA_3301:
      case TOSHIBA_3401:
         rc = Tosh_Read_2352 (pUnitCB, (ULONG) ul_LBA.dword,
                                            transfer_count, ppDataBuff);
         break;

      case SONY_561:
         rc = Sony_Read_2352 (pUnitCB, (ULONG) ul_LBA.dword,
                                        transfer_count, ppDataBuff);
         break;

      default:
         if (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
            rc = ATAPI_Read_2352 (pUnitCB, (ULONG) ul_LBA.dword,
                                           transfer_count, ppDataBuff);
         else
            rc = CD_Read_2352 (pUnitCB, (ULONG) ul_LBA.dword,
                                           transfer_count, ppDataBuff);

         if (rc == STDON && raw_block_size == 2340)
            PadRaw2340 (pRP->DataPacket, pParmPacket->transfer_count);

         break;
   }

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

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Read_2352
 *
 * DESCRIPTION   = Read default CD-ROM Mode 1 sector with blocksize = 2352
 *
 *                 USHORT CD_Read_2352  (NPUNITCB pUnitCB, ULONG LBA,
 *                                   USHORT transfer_count, ULONG ppDataBuff)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 LBA              - LBA
 *                 transfer_count   - count of sectors to transfer
 *                 ppDataBuff       - phys addr of data buffer
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Read_2352 (pUnitCB, LBA, transfer_count, ppDataBuff)

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

{
   USHORT rc, raw_sector_size;

   raw_sector_size = pUnitCB->DeviceInfo.raw_block_size;

   /*
   ** If the current block size is not a long sector, then issue
   ** a SCSI Mode Select command to set the block size.
   */
   if ((pUnitCB->DeviceInfo.current_block_size != raw_sector_size) &
      !(pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI))
   {
      rc = ChainModeSelectRead (pUnitCB, LBA, transfer_count, ppDataBuff,
                                              raw_sector_size, 0);
   }
   else
   {
      rc = ReadSector (pUnitCB, LBA, transfer_count,
                                ppDataBuff, raw_sector_size);
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = ATAPI_Read_2352
 *
 * DESCRIPTION   = Read 2352 byte sector on ATAPI device
 *
 *                 USHORT ATAPI_Read_2352  (NPUNITCB pUnitCB, ULONG LBA,
 *                                   USHORT transfer_count, ULONG ppDataBuff)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 LBA              - LBA
 *                 transfer_count   - count of sectors to transfer
 *                 ppDataBuff       - phys addr of data buffer
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ATAPI_Read_2352 (pUnitCB, LBA, transfer_count, ppDataBuff)

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

{
   USHORT rc;
   NPIORB_CDB pIORB;

   /*
   ** Issue ATAPI Read CD command which should read any target sector,
   ** including 2352 byte Mode 1, 2352 byte Mode 2 Form 1, 2352 byte
   ** Mode 2 Form 2, and 2352 byte CD-DA
   */

   ATAPI_BuildCDB_ReadCD  (pUnitCB, LBA, transfer_count,
                                      ppDataBuff, (NPIORB_CDB FAR *) &pIORB);


   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}



/****************************************************************************
 *
 * FUNCTION NAME = GetRawBlockSize
 *
 * DESCRIPTION   = Get the max raw block size the device supports.
 *
 *                 USHORT GetRawBlockSize (NPUNITCB, pUnitCB,
 *                                        (USHORT FAR *) raw_block_size)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT GetRawBlockSize (pUnitCB)

NPUNITCB pUnitCB;
{
   USHORT rc;
   NPIORB_CDB pIORB;



   /*
   **  Assume all ATAPI drives support returning a full 2352 byte sector.
   */

   if (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
   {
      pUnitCB->DeviceInfo.raw_block_size = 2352;
      return(STDON);
   }

   /*
   ** First See if a block size of 2352 bytes is supported
   */
   rc = Submit_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2352);

   if (rc == STDON)
      pUnitCB->DeviceInfo.raw_block_size = 2352;
   else
   {
      /*
      ** 2352 not supported, try 2340
      */
      rc = Submit_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2340);
      if (rc == STDON)
         pUnitCB->DeviceInfo.raw_block_size = 2340;
   }

   /*
   ** Put the block size back to 2048
   */
   rc = Submit_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2048);
   if (rc == STDON)
   {
      pUnitCB->DeviceInfo.current_density = CD_DENSITY_DEFAULT;
      pUnitCB->DeviceInfo.current_block_size = 2048;
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_ReadLongPre
 *
 * DESCRIPTION   = Read long.
 *
 *                 USHORT CD_ReadLongPre(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ReadLongPre (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   return ( CD_ReadPrefetch (pRP, pUnitCB) );
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_ReturnVolumeSize
 *
 * DESCRIPTION   = Get volume size,
 *
 *                 USHORT CD_ReturnVolumeSize(PRP_GENIOCTL pRP,
 *                                                 NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_ReturnVolumeSize (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT     rc;
   ULONG      volume_size;


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

   rc = GetVolumeSize (pUnitCB, &volume_size);

   if (rc == STDON)
      ((struct ReturnVolumeSize_Data FAR *)pRP->DataPacket)->volume_size =
                                                             volume_size;

   return(rc);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_GetUPC
 *
 * DESCRIPTION   = Get UPC.
 *
 *                 USHORT CD_GetUPC(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_GetUPC (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc, i;
   NPIORB_CDB pIORB;
   struct UPCCode_Data FAR *pDataPkt;
   union CDROM_SubChannel_Info NEAR *q_data;
   USHORT GotUPC = NO;

   pDataPkt = (struct UPCCode_Data FAR *) pRP->DataPacket;

   if ( ! (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_UPC) )
      return(STDON + STERR + ERROR_I24_BAD_COMMAND);

   /*
   ** First, issue ReadSubChannel for Current Position
   */
   ClearCheckCondition (pUnitCB);

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

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   if (rc == STDON)
   {
      q_data = (union CDROM_SubChannel_Info NEAR *) pIORB->CDB_data;

      pDataPkt->adr_control = (q_data->current_position.control << 4) |
                               q_data->current_position.ADR;

/*
**    if (pDataPkt->adr_control & 0x0F)
**        pDataPkt->adr_control = ((q_data->current_position.control << 4) |
**                                  MODE_2);
*/
      pDataPkt->zero = 0;
      pDataPkt->aframe = q_data->current_position.abs_address.redbook.frame;
   }

   FreeIORB (pUnitCB, pIORB);

   /*
   ** Issue SCSI Read SubChannel - Get UPC Code
   */
   if (rc == STDON)
   {
      ClearCheckCondition (pUnitCB);

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

      rc = SubmitIORB_Wait(pUnitCB, pIORB);

      if (rc == STDON)
      {
         q_data = (union CDROM_SubChannel_Info NEAR *) pIORB->CDB_data;

         /*
         ** Make sure media catalog number field is valid
         */
         if (q_data->media_cat_number.mcval)
         {
            if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_UPC_IN_BCD)
            {
               pDataPkt->upc_ean_code.upc_byte0 =
                        q_data->media_cat_number.media_catalog_no[0];

               pDataPkt->upc_ean_code.upc_byte1 =
                        q_data->media_cat_number.media_catalog_no[1];

               pDataPkt->upc_ean_code.upc_byte2 =
                        q_data->media_cat_number.media_catalog_no[2];

               pDataPkt->upc_ean_code.upc_byte3 =
                        q_data->media_cat_number.media_catalog_no[3];

               pDataPkt->upc_ean_code.upc_byte4 =
                        q_data->media_cat_number.media_catalog_no[4];

               pDataPkt->upc_ean_code.upc_byte5 =
                        q_data->media_cat_number.media_catalog_no[5];

               pDataPkt->upc_ean_code.upc_byte6 =
                        q_data->media_cat_number.media_catalog_no[6];
            }
            else
            {
               if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_UPC_IN_ASCII)
               {
                  for (i = 0; i <= 13; i++)
                     q_data->media_cat_number.media_catalog_no[i] -= 0x30;
               }


               pDataPkt->upc_ean_code.upc_byte0 =
                        q_data->media_cat_number.media_catalog_no[0] << 4 |
                        q_data->media_cat_number.media_catalog_no[1];

               pDataPkt->upc_ean_code.upc_byte1 =
                        q_data->media_cat_number.media_catalog_no[2] << 4 |
                        q_data->media_cat_number.media_catalog_no[3];

               pDataPkt->upc_ean_code.upc_byte2 =
                        q_data->media_cat_number.media_catalog_no[4] << 4 |
                        q_data->media_cat_number.media_catalog_no[5];

               pDataPkt->upc_ean_code.upc_byte3 =
                        q_data->media_cat_number.media_catalog_no[6] << 4 |
                        q_data->media_cat_number.media_catalog_no[7];

               pDataPkt->upc_ean_code.upc_byte4 =
                        q_data->media_cat_number.media_catalog_no[8] << 4 |
                        q_data->media_cat_number.media_catalog_no[9];

               pDataPkt->upc_ean_code.upc_byte5 =
                        q_data->media_cat_number.media_catalog_no[10] << 4 |
                        q_data->media_cat_number.media_catalog_no[11];

               pDataPkt->upc_ean_code.upc_byte6 =
                        q_data->media_cat_number.media_catalog_no[12] << 4 |
                        q_data->media_cat_number.media_catalog_no[13];
            }
            GotUPC = YES;
         }
      }
      FreeIORB (pUnitCB, pIORB);
   }

   /*
   ** Add error checking code
   */
   if (GotUPC == NO)
   {
      pDataPkt->adr_control = 0;
      pDataPkt->upc_ean_code.upc_byte0 = 0;
      pDataPkt->upc_ean_code.upc_byte1 = 0;
      pDataPkt->upc_ean_code.upc_byte2 = 0;
      pDataPkt->upc_ean_code.upc_byte3 = 0;
      pDataPkt->upc_ean_code.upc_byte4 = 0;
      pDataPkt->upc_ean_code.upc_byte5 = 0;
      pDataPkt->upc_ean_code.upc_byte6 = 0;
      pDataPkt->zero = 0;
      pDataPkt->aframe = 0;
   }
   /*
   ** Clear media changed check condition on Sony CDU-541 after
   ** UPC command issued.
   */
   ClearCheckCondition (pUnitCB);

   if (pUnitCB->DeviceInfo.product_id_code == SONY_541)
      pUnitCB->Flags &= ~UCF_UNCERTAIN_MEDIA;

   return (STDON);
}
