/*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 = CDSONY.C
 *
 * DESCRIPTIVE NAME = Vendor unique command processing for Sony
 *                    SCSI-2 CD-ROM Drives
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "cdh.h"
#include "cdsony.h"


/****************************************************************************
 *
 * FUNCTION NAME = Sony_ReadCDXA
 *
 * DESCRIPTION   = Issue Sony Read-XA Command
 *
 *       USHORT Sony_ReadCDXA (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 Sony_ReadCDXA(pUnitCB,LBA,transfer_count,ppDataBuff,block_length)

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

{
   USHORT rc, format;
   NPIORB_CDB pIORB = 0;

   if (block_length == 2048)
     format = CDXA_FORMAT_2048;
   else
     format = CDXA_FORMAT_2352;

   Sony_BuildCDB_Read_CDXA  (pUnitCB, LBA, block_length,
                                  transfer_count, ppDataBuff, format,
                                  (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Sony_Read_2048
 *
 * DESCRIPTION   = Read 2048 byte sectors for Sony 561
 *
 *      USHORT Sony_Read_2048      (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 Sony_Read_2048 (pUnitCB, LBA, transfer_count, ppDataBuff)

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

{
   USHORT mode, rc;

   if (LBA == 0L && transfer_count == 1)
   {
      if ( (rc = GetSectorMode (pUnitCB, (USHORT FAR *) &mode)) & STERR)
         return (rc);

      if (mode == 2)
         pUnitCB->DeviceInfo.current_disk_type = DISK_TYPE_CDXA;
      else
         pUnitCB->DeviceInfo.current_disk_type = DISK_TYPE_CDROM;

   }

   if  (pUnitCB->DeviceInfo.current_disk_type == DISK_TYPE_CDDA)
       return (STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);

   /*
   ** Issue mode select if block length is not right
   */
   if   (pUnitCB->DeviceInfo.current_block_size != 2048)
      rc = Submit_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2048);


   if (pUnitCB->DeviceInfo.current_disk_type == DISK_TYPE_CDXA)
   {
      rc = Sony_ReadCDXA (pUnitCB, LBA, transfer_count, ppDataBuff, 2048);
   }
   else
   {
      rc = ReadSector (pUnitCB, LBA, transfer_count, ppDataBuff, 2048);
   }
   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Sony_ChainModeSelectRead_CDDA
 *
 * DESCRIPTION   = Chain Mode Select and Read CDDA Commands
 *
 *    USHORT Sony_ChainModeSelectRead_CDDA (NPUNITCB pUnitCB, ULONG LBA,
 *                                USHORT transfer_count, ULONG ppDataBuff,
 *                                USHORT density, USHORT block_length,
 *                                USHORT type)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 LBA              - LBA
 *                 transfer_count   - count of sectors to transfer
 *                 ppDataBuff       - phys addr of data buffer
 *                 density          - density code
 *                 block_length     - block_length
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Sony_ChainModeSelectRead_CDDA (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, pModeIORB = 0;
   NPIORB_DMWORK pDMWork;

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

   /*
   ** If transfer count < 255 sectors, issue Read (6) else issue Read (10)
   */
   Sony_BuildCDB_Read_CDDA  (pUnitCB, LBA, 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_block_size = block_length;

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

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Sony_ReadCDDA
 *
 * DESCRIPTION   = Sony Read CD-DA Sectors
 *
 *       USHORT Sony_ReadCDDA (NPUNITCB pUnitCB, ULONG ul_LBA,
 *                          USHORT transfer_count, ULONG ppDataBuff,
 *                          USHORT block_length)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 ul_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 Sony_ReadCDDA (pUnitCB, LBA, transfer_count, ppDataBuff)

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

{
   USHORT rc;
   NPIORB_CDB pIORB = 0;

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

   rc = SubmitIORB_Wait(pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = Sony_Read_2352
 *
 * DESCRIPTION   = Read 2352 byte sectors for Sony CDU-561
 *
 *      USHORT Sony_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 Sony_Read_2352 (pUnitCB, LBA, transfer_count, ppDataBuff)

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

{
   USHORT last_cmd, rc;

   /*
   ** Make sure block length = 2352.  For efficiency, try issuing the same
   ** read command as the last one.  Change read command type if error, and
   ** retry the read.
   */
   last_cmd = pUnitCB->DeviceInfo.last_cmd;

   if (pUnitCB->DeviceInfo.current_block_size != 2352)
   {
      if (last_cmd == SONY_READ_CDDA)
      {
          rc = Sony_ChainModeSelectRead_CDDA (pUnitCB, LBA, transfer_count,
                                                       ppDataBuff, 2352);
      }
      else
      {
          rc = ChainModeSelectRead (pUnitCB, LBA, transfer_count, ppDataBuff,
                                             2352, 0);
      }
   }
   else
   {
      if (pUnitCB->DeviceInfo.last_cmd == SONY_READ_CDDA)
          rc = Sony_ReadCDDA (pUnitCB, LBA, transfer_count, ppDataBuff);
      else
          rc = ReadSector (pUnitCB, LBA, transfer_count, ppDataBuff, 2352);
   }

   /*
   ** If a read error occurred, try changing the read command and reissue.
   */
   if  (rc == STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND)
   {
      if (last_cmd == SONY_READ_CDDA)
      {
          rc = ReadSector (pUnitCB, LBA, transfer_count, ppDataBuff, 2352);
          pUnitCB->DeviceInfo.last_cmd = 0;
      }
      else
      {
          rc = Sony_ReadCDDA (pUnitCB, LBA, transfer_count, ppDataBuff);
          pUnitCB->DeviceInfo.last_cmd = SONY_READ_CDDA;
      }
   }
   return(rc);
}


;/***************************************************************************
;*
;* FUNCTION NAME = Sony_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 Sony 561.
;*
;*   USHORT Sony_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 Sony_GetLastSessionAddr (pUnitCB, session_addr)

NPUNITCB pUnitCB;
ULONG    FAR *session_addr;

{
   USHORT rc;
   NPIORB_CDB pIORB;
   struct ReadTOC_Data NEAR *pCDBData;
   struct CDB_ReadTOC *pCDB;               /* ptr to CDB               */
   union  AddressType last_session;

   BuildCDB_ReadTOC (pUnitCB, 0, 1, (NPIORB_CDB FAR *) &pIORB);

   pCDB = (struct CDB_ReadTOC NEAR *) &pIORB->CDB;
   pCDBData = (struct ReadTOC_Data NEAR *) pIORB->CDB_data;

   /*
   ** Set Format field in CDB to return multisession info for Read TOC
   */
   pCDB->control |= 0x40;

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   if (rc == STDON)
   {
      /*
      ** If first session != last session, then multisession disk
      */
      if  (pCDBData->toc_hdr.first_track != pCDBData->toc_hdr.last_track)
      {
         last_session.ul_redbook.min =
                         pCDBData->toc_descriptor[0].abs_address.redbook.min;

         last_session.ul_redbook.sec =
                         pCDBData->toc_descriptor[0].abs_address.redbook.sec;

         last_session.ul_redbook.frame =
                         pCDBData->toc_descriptor[0].abs_address.redbook.frame;

         last_session.ul_redbook.zero = 0;

         last_session.dword = RedBookToHSG (last_session.dword);

         *session_addr = last_session.dword;
      }
      else
      {
        rc = STDON + STERR;
      }
   }
   FreeIORB (pUnitCB, pIORB);
   return (rc);
}

