/*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 = CDIOC81.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"

/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioChannelCtrl
 *
 * DESCRIPTION   = Audio Channel Control.
 *
 *       Provides playback control of audio information on the disk.  This
 *       includes assigning input channels to output channels and controlling
 *       the volume of each output channel.
 *
 *       USHORT CD_ChannelControl (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioChannelCtrl (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   BOOL   playing;
   NPIORB_CDB pIORB;

   struct AudioChannelControl_Data FAR *pDataPkt;
   struct Channel *channel;
   struct Audio   *audio;
   struct Status  *audio_status;

   union AddressType start_red, end_red;

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

   audio = &pUnitCB->DeviceInfo.Audio;
   channel = &audio->channel;
   audio_status = &audio->status;

   /*
   ** Copy the data to the UnitCB for channels 0 and 1
   */
   channel->input_0  = pDataPkt->input_0;
   channel->volume_0 = pDataPkt->volume_0;
   channel->input_1  = pDataPkt->input_1;
   channel->volume_1 = pDataPkt->volume_1;

   /*
   ** Only 2 channels supported at this time, so check to make sure
   ** channels above 1 are not specified.
   */
   if (channel->input_0 > 1 || channel->input_1 > 1)
   {
      channel->input_0  = 0;
      channel->input_1  = 1;
      channel->volume_0 = 0xFF;
      channel->volume_1 = 0xFF;
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);
   }

   /*
   ** Set volume setting for both channels the same.
   */
/*
** if (channel->volume_0 && channel->volume_1)
** {
**    if (channel->volume_0 > channel->volume_1)
**       channel->volume_1 = channel->volume_0;
**    else
**       channel->volume_0 = channel->volume_1;
** }
*/
   /*
   ** If the drive doesn't support receiving an audio control command
   ** while it's playing, then pause the audio, issue the audio control
   ** command and resume the audio.
   */
   rc = GetPlayStatus (pUnitCB, &playing);

   if (rc == STDON && playing &&
                      (audio->capabilities & DCAPS_NO_AUDIO_CTRL_DURING_PLAY))
   {
      /*
      ** Pause the play operation
      */
      rc = CD_Stop (pRP, pUnitCB);

      /*
      ** Issue the audio control command
      */
      BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
      pIORB->filter_workspace[0] = (UCHAR) playing;
      rc = SubmitIORB_Wait (pUnitCB, pIORB);
      FreeIORB (pUnitCB, pIORB);

      /*
      ** Resume playing
      */
      rc = CD_Resume (pRP, pUnitCB);
   }
   else
   {
      /*
      ** Issue Mode Select to set Audio Control Page info
      */
      BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
      pIORB->filter_workspace[0] = (UCHAR) playing;
      rc = SubmitIORB_Wait (pUnitCB, pIORB);
      FreeIORB (pUnitCB, pIORB);
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Play
 *
 * DESCRIPTION   = Play
 *
 *                 Play the selected audio tracks until the selection is done
 *                 or until a stop command is issued.
 *
 *                 USHORT CD_Play (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Play (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   BOOL   playing;
   NPIORB_CDB pIORB;

   union  AddressType start_red, start_hsg, end_red, end_hsg, last_sector;
   struct PlayAudio FAR *pParmPkt;
   struct Audio *audio;
   struct Status *audio_status;

   pParmPkt = (struct PlayAudio FAR *) pRP->ParmPacket;


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

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

   if (playing)
      return (STDON + STERR + ERROR_I24_DEVICE_IN_USE);


   switch(pParmPkt->address_mode)
   {
      case CDROM_HSG_MODE:
          start_hsg.dword = pParmPkt->start_sector;
          end_hsg.dword   = pParmPkt->end_sector;
          start_red.dword = HSGtoRedBook (start_hsg.dword);
          end_red.dword   = HSGtoRedBook (end_hsg.dword);
          break;

      case CDROM_REDBOOK_MODE:
          start_red.dword = pParmPkt->start_sector;
          end_red.dword   = pParmPkt->end_sector;
          start_hsg.dword = RedBooktoHSG (start_red.dword);
          end_hsg.dword   = RedBooktoHSG (end_red.dword);
          break;

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

   if ( (pUnitCB->DeviceInfo.leadout.dword == end_red.dword) &&
        (end_red.dword != 0) )
   {
      if (end_red.ul_redbook.frame != 0)
         end_red.ul_redbook.frame --;
      else if (end_red.ul_redbook.sec != 0)
      {
         end_red.ul_redbook.sec --;
         end_red.ul_redbook.frame = 74;
      }
      else
      {
         end_red.ul_redbook.min--;
         end_red.ul_redbook.sec = 59;
         end_red.ul_redbook.frame = 74;
      }
   }

   if ( (ULONG) start_hsg.dword < 0L )
      return (STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);


   audio = &pUnitCB->DeviceInfo.Audio;
   audio_status = &audio->status;

   /*
   ** Issue Play command
   */
   BuildCDB_PlayAudio_MSF (pUnitCB, start_red, end_red,
                                               (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc == STDON)
   {
      pUnitCB->DeviceInfo.playing = TRUE;
      audio_status->paused = FALSE;

      audio_status->last_start_location.min   = start_red.ul_redbook.min;
      audio_status->last_start_location.sec   = start_red.ul_redbook.sec;
      audio_status->last_start_location.frame = start_red.ul_redbook.frame;

      audio_status->last_end_location.min   = end_red.ul_redbook.min;
      audio_status->last_end_location.sec   = end_red.ul_redbook.sec;
      audio_status->last_end_location.frame = end_red.ul_redbook.frame;
   }
   else if (rc == STDON + STERR + ERROR_I24_BAD_COMMAND)
   {
      rc = STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND;
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Stop
 *
 * DESCRIPTION   = Stop play
 *
 *                 Cancel any active play request.  It is not an error if the
 *                 drive is not currently playing.  The ending location is
 *                 saved for a subsequent resume operation.
 *
 *                 USHORT CD_Stop (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Stop (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   BOOL   playing;
   NPIORB_CDB pIORB;
   struct Status *audio_status;
   struct SubChannel_Position NEAR *pCDBData;

   audio_status = &pUnitCB->DeviceInfo.Audio.status;

   /*
   ** Simply return without error if not currently playing
   */
   rc = GetPlayStatus (pUnitCB, &playing);
   if ( ! playing)
      return (STDON);

   /*
   ** Issue SCSI Pause Command to cancel play operation.
   */
   BuildCDB_PauseResume (pUnitCB, CDBF_PAUSE, (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);

   if (rc & STERR)
      return(rc);

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

   rc = SubmitIORB_Wait(pUnitCB, pIORB);


   if (rc == STDON)
   {
      pCDBData = (struct SubChannel_Position FAR *) pIORB->CDB_data;

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

      audio_status->paused = TRUE;
      pUnitCB->DeviceInfo.playing = FALSE;
   }

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Resume
 *
 * DESCRIPTION   = Resume Play
 *
 *                 Resume playing audio tracks after play has been interrupted
 *                 with the Stop Audio command.  An error is returned if there
 *                 was no previous Stop command.  The Resume is converted to a
 *                 Play command from the last play address when paused.
 *
 *                 USHORT CD_Resume (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_Resume (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct Status *audio_status;
   struct Audio  *audio;

   union AddressType start_red, end_red;

   audio = &pUnitCB->DeviceInfo.Audio;
   audio_status = &audio->status;

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

   if ( ! audio_status->paused )
      return (STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND);

   /*
   ** Resume is simply a play from the last address when paused to the
   ** last ending play address.
   */
   start_red.ul_redbook.min   = audio_status->last_start_location.min;
   start_red.ul_redbook.sec   = audio_status->last_start_location.sec;
   start_red.ul_redbook.frame = audio_status->last_start_location.frame;
   start_red.ul_redbook.zero  = 0;

   end_red.ul_redbook.min   = audio_status->last_end_location.min;
   end_red.ul_redbook.sec   = audio_status->last_end_location.sec;
   end_red.ul_redbook.frame = audio_status->last_end_location.frame;
   end_red.ul_redbook.zero  = 0;

   BuildCDB_PlayAudio_MSF (pUnitCB, start_red, end_red,
                                               (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   FreeIORB (pUnitCB, pIORB);


   if ( (rc == STDON) || (rc == STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA) )
      audio_status->paused = FALSE;

   pUnitCB->DeviceInfo.playing = TRUE;

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioChannelInfo
 *
 * DESCRIPTION   = Return audio channel information
 *
 *       Return the current settings of the audio channel controls.  These
 *       are either the default settings or those set with the Audio Channel
 *       control IOCTL.
 *
 *       USHORT CD_AudioChannelInfo (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioChannelInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   struct Channel *channel;

   channel = &pUnitCB->DeviceInfo.Audio.channel;

   * (struct AudioChannelControl_Data FAR *)pRP->DataPacket =

   * (struct AudioChannelControl_Data NEAR *) channel;

  return(STDON);

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioDiskInfo
 *
 * DESCRIPTION   = Return audio disk information.
 *
 *       Returns the first and last track numbers and the Red Book address
 *       for the lead-out track.  This information is read from the TOC info
 *       on the Q-channel on the lead-in track.  The first and last number are
 *       binary values and not BCD.
 *
 *       USHORT CD_AudioDiskInfo (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioDiskInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct AudioDiskInfo_Data FAR *pDataPkt;
   struct ReadTOC_Data FAR *pCDBData;

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


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

   /*
   ** Issue a SCSI Read TOC command to retrieve the information.
   */
   BuildCDB_ReadTOC (pUnitCB,LEAD_OUT_TRACK_NUMBER,1,(NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   if (rc & STERR)
   {
      if (ReadTOC_Leadout (pUnitCB, pIORB) & STERR)
      {
         FreeIORB (pUnitCB, pIORB);
         return(rc);
      }
   }
   /*
   ** Copy the data from the CDB data area to the IOCTL data packet
   */
   pCDBData = (struct ReadTOC_Data FAR *) pIORB->CDB_data;

   pDataPkt->lowest_track  = pCDBData->toc_hdr.first_track;
   pDataPkt->highest_track = pCDBData->toc_hdr.last_track;

   pDataPkt->frame = pCDBData->toc_descriptor[0].abs_address.redbook.frame;
   pDataPkt->sec   = pCDBData->toc_descriptor[0].abs_address.redbook.sec;
   pDataPkt->min   = pCDBData->toc_descriptor[0].abs_address.redbook.min;
   pDataPkt->zero  = 0;

   pUnitCB->DeviceInfo.leadout.ul_redbook.min   = pDataPkt->min;
   pUnitCB->DeviceInfo.leadout.ul_redbook.sec   = pDataPkt->sec;
   pUnitCB->DeviceInfo.leadout.ul_redbook.frame = pDataPkt->frame;

   if (pUnitCB->DeviceInfo.product_id_code == NEC_260_17B)
   {
      pDataPkt->frame = BCDtoBinary(pDataPkt->frame);
      pDataPkt->sec   = BCDtoBinary(pDataPkt->sec);
      pDataPkt->min   = BCDtoBinary(pDataPkt->min);
   }


   FreeIORB (pUnitCB, pIORB);

   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioTrackInfo
 *
 * DESCRIPTION   = Return audio track information.
 *
 *      Given a track number, return the Red Book address for the starting
 *      point of the track, and the track control information for that track.
 *
 *      USHORT CD_AudioTrackInfo (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioTrackInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct AudioTrackInfo_Data FAR *pDataPkt;
   struct ReadTOC_Data FAR *pCDBData;

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


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

   /*
   ** Issue a SCSI Read TOC command to retrieve the information.
   */
   BuildCDB_ReadTOC (pUnitCB,
                 ((struct AudioTrackInfo FAR *)pRP->ParmPacket)->track_number,
                 1,
                 (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   /*
   ** If there's an error reading the TOC for the leadout track, then
   ** re-read 2 TOC entries starting with the last valid track number.
   */
   if ( (rc & STERR) &&
        ( ((struct AudioTrackInfo FAR *)pRP->ParmPacket)->track_number
                                                   == LEAD_OUT_TRACK_NUMBER) )
   {
      rc = ReadTOC_Leadout (pUnitCB, pIORB);
   }

   /*
   ** Copy the data from the CDB data area to the IOCTL data packet
   */
   if (rc == STDON)
   {
      pCDBData = (struct ReadTOC_Data FAR *) pIORB->CDB_data;

      pDataPkt->frame = pCDBData->toc_descriptor[0].abs_address.redbook.frame;
      pDataPkt->sec   = pCDBData->toc_descriptor[0].abs_address.redbook.sec;
      pDataPkt->min   = pCDBData->toc_descriptor[0].abs_address.redbook.min;
      pDataPkt->zero  = 0;

      if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_NO_ADR_RETURNED)
         pCDBData->toc_descriptor[0].ADR = ADR_CURRENT_POSITION;

      pDataPkt->control =  pCDBData->toc_descriptor[0].ADR |
                           (pCDBData->toc_descriptor[0].control << 4);


      if (pUnitCB->DeviceInfo.product_id_code == NEC_260_17B)
      {
         pDataPkt->frame = BCDtoBinary(pDataPkt->frame);
         pDataPkt->sec   = BCDtoBinary(pDataPkt->sec);
         pDataPkt->min   = BCDtoBinary(pDataPkt->min);
      }
   }

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = ReadTOC_Leadout
 *
 * DESCRIPTION   = Read the TOC for the leadout track;
 *
 *     The Hitachi 3750 returns a check condition if a ReadTOC command is
 *     issued for track_num = 0xAA, the leadout track. Reading the TOC for
 *     the leadout track is valid under SCSI-II.  The workaround is to issue
 *     the ReadTOC command for 2 TOC entries, starting with the last valid
 *     track_num before the lead out track. The last entry is the leadout track.
 *
 *     USHORT RetryTOCRead  (NPUNITCB pUnitCB, NPIORB pIORB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 pIORB            - Pointer to IORB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ReadTOC_Leadout (pUnitCB, pIORB)

NPUNITCB   pUnitCB;
NPIORB_CDB pIORB;
{
   NPIORB_CDB pIORB2;
   struct ReadTOC_Data FAR *pCDBData1;
   struct ReadTOC_Data FAR *pCDBData2;
   USHORT rc = STDON + STERR;

   if ( (pIORB->status_block.Flags & STATUS_SENSEDATA_VALID) &&
        ((pIORB->sense_data.error_code == 0x70) ||
         (pIORB->sense_data.error_code == 0x71)) &&

           pIORB->sense_data.sense_key == SCSI_SK_ILLEGALREQ)
/*      && pIORB->sense_data.additional_sense_code == ASC_INVALID_FIELD)   */
   {
      BuildCDB_ReadTOC (pUnitCB, 1, 1, (NPIORB_CDB FAR *) &pIORB2);

      rc = SubmitIORB_Wait (pUnitCB, pIORB2);

      if (rc == STDON)
      {
         pCDBData1 = (struct ReadTOC_Data FAR *) pIORB->CDB_data;
         pCDBData2 = (struct ReadTOC_Data FAR *) pIORB2->CDB_data;

         FreeIORB(pUnitCB, pIORB2);

         BuildCDB_ReadTOC (pUnitCB,
                           pCDBData2->toc_hdr.last_track,
                           2,
                           (NPIORB_CDB FAR *) &pIORB2);

         rc = SubmitIORB_Wait (pUnitCB, pIORB2);

         if (rc == STDON)
         {
           pCDBData1->toc_hdr = pCDBData2->toc_hdr;
           pCDBData1->toc_descriptor[0] = pCDBData2->toc_descriptor[1];
         }
      }
      FreeIORB(pUnitCB, pIORB2);
   }
   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioQChannelInfo
 *
 * DESCRIPTION   = Return audio Q-channel information.
 *
 *        Reads and returns the most current address information from the
 *        Q-channel.  This command does not interrupt the present status
 *        of the drive as one of its intended purposes is to monitor the
 *        location of the read head while playing audio tracks.
 *
 *        USHORT CD_QChannelInfo (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioQChannelInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct AudioQChannelInfo_Data FAR *pDataPkt;
   struct SubChannel_Position FAR *pCDBData;

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

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

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

   /*
   ** Issue a SCSI Read TOC command to retrieve the information.
   */
   BuildCDB_ReadSubChannel(pUnitCB, RSC_CURRENT_POSITION,
                                    (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   if (rc & STERR)
   {
      FreeIORB (pUnitCB, pIORB);
      return(rc);
   }
   /*
   ** Copy the data from the CDB data area to the IOCTL data packet
   */
   pCDBData = (struct SubChannel_Position FAR *) pIORB->CDB_data;

   if (pUnitCB->DeviceInfo.product_id_code == NEC_260_17B)
   {
      pDataPkt->min    = BCDtoBinary(pCDBData->rel_address.redbook.min);
      pDataPkt->sec    = BCDtoBinary(pCDBData->rel_address.redbook.sec);
      pDataPkt->frame  = BCDtoBinary(pCDBData->rel_address.redbook.frame);

      pDataPkt->amin   = BCDtoBinary(pCDBData->abs_address.redbook.min);
      pDataPkt->asec   = BCDtoBinary(pCDBData->abs_address.redbook.sec);
      pDataPkt->aframe = BCDtoBinary(pCDBData->abs_address.redbook.frame);
   }
   else
   {
      pDataPkt->min    = pCDBData->rel_address.redbook.min;
      pDataPkt->sec    = pCDBData->rel_address.redbook.sec;
      pDataPkt->frame  = pCDBData->rel_address.redbook.frame;

      pDataPkt->amin   = pCDBData->abs_address.redbook.min;
      pDataPkt->asec   = pCDBData->abs_address.redbook.sec;
      pDataPkt->aframe = pCDBData->abs_address.redbook.frame;
   }

   pDataPkt->zero   = 0;

   if (pUnitCB->DeviceInfo.Audio.capabilities & DCAPS_NO_ADR_RETURNED)
      pCDBData->ADR = ADR_CURRENT_POSITION;

   pDataPkt->control = pCDBData->ADR | (pCDBData->control << 4);

   pDataPkt->tno   = BinaryToBCD (pCDBData->track_number);
   pDataPkt->point = BinaryToBCD (pCDBData->index_number);

   FreeIORB (pUnitCB, pIORB);

   return(STDON);
}


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

USHORT CD_AudioSubChannelInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   return(STDON + STERR + ERROR_I24_BAD_COMMAND);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_AudioStatusInfo
 *
 * DESCRIPTION   = Return audio status information.
 *
 *       Returns the Audio Paused bit and the starting and ending locations
 *       of the last play or the next resume.
 *
 *       USHORT CD_AudioStatusInfo (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_AudioStatusInfo (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   NPIORB_CDB pIORB;

   struct AudioStatusInfo_Data FAR *pDataPkt;
   struct SubChannel_Position  FAR *pCDBData;

   struct Status *audio_status;
   struct Audio  *audio;

   struct ul_RedBookAddress start, end;


   pDataPkt = (struct AudioStatusInfo_Data FAR *) pRP->DataPacket;
   audio = &pUnitCB->DeviceInfo.Audio;
   audio_status = &audio->status;

   /*
   ** Issue a SCSI Read SubChannel command to retrieve the information.
   */
   BuildCDB_ReadSubChannel(pUnitCB, RSC_CURRENT_POSITION,
                                    (NPIORB_CDB FAR *) &pIORB);

   rc = SubmitIORB_Wait (pUnitCB, pIORB);

   if (rc == STDON)
   {
      /*
      ** Copy the data to the IOCTL data packet
      */
      pCDBData = (struct SubChannel_Position FAR *) pIORB->CDB_data;

      start.frame = audio_status->last_start_location.frame;
      start.sec   = audio_status->last_start_location.sec;
      start.min   = audio_status->last_start_location.min;
      start.zero  = 0;

      end.frame =  audio_status->last_end_location.frame;
      end.sec   =  audio_status->last_end_location.sec;
      end.min   =  audio_status->last_end_location.min;
      end.zero  =  0;

      pDataPkt->audio_status = *(USHORT *) audio_status;
      pDataPkt->last_start_location = *(ULONG FAR *) &start;
      pDataPkt->last_end_location   = *(ULONG FAR *) &end;

      if (pUnitCB->Flags & UCF_UNCERTAIN_MEDIA)
         rc = STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA;
   }

   FreeIORB (pUnitCB, pIORB);

   return(rc);
}

