/*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 = SOIORB.C
 *
 * DESCRIPTIVE NAME = IORB handling for SONY Filter
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   Filters SCSI-2 Command Descriptor Blocks and conters
 *             them to the vendor unique Sony Command Descriptor
 *             Blocks.
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include <flthdr.h>

/*
** FUNCTION PROTOTYPES
*/

USHORT NotifyRead6 (PIORB_CDB pIORB);
USHORT NotifyChannelControl (PIORB_CDB pIORB);
USHORT NotifyPlayStatus (PIORB_CDB pIORB);
USHORT NotifyReadSub (PIORB_CDB pIORB);
USHORT NotifyReadTOC (PIORB_CDB pIORB);

ULONG   HSGtoRedBook (ULONG);

/*
** CODE STARTS HERE
*/

/****************************************************************************
 *
 * FUNCTION NAME = Filter_Read_6
 *
 * DESCRIPTION   = This routine checks for Read requests for LBA of 0 with a
 *                 length of 1.  We know that this is the very first request
 *                 that will be sent to the drive so we can issue a mode sense
 *                 command to determine what addressing mode the drive is
 *                 currently in. (MSF or LBA)
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_Read_6 (PIORB_CDB pIORB)
{
   USHORT   i;

   struct CDB_Read_6      FAR *pCDB1;
   struct CDB_ModeSense_6 FAR *pCDB2;
   PIORB_FLTWORK          pFilter_workspace;
   NPUNITCB               pUnitCB;

   pCDB1 = (struct CDB_Read_6 FAR *) pIORB->apt.pControllerCmd;
   pCDB2 = (struct CDB_ModeSense_6 FAR *) pCDB1;

   if ( pCDB1->LBA.word != 0 || pCDB1->transfer_length != 1 )
      return CALL_ADD_AND_RET;

   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;
   pUnitCB = pFilter_workspace->pUnitCB;

   /*
   ** We only need to send the Mode Sense request once.
   */
   if ( pUnitCB->unit_flags & MODE_SENSE )
      return CALL_ADD_AND_RET;

   /*
   ** Need to store the physical data pointer for the read request so that
   ** we can send a Mode Sense request down.
   */
   pUnitCB->read_buffer = pIORB->apt.pSGList->ppXferBuf;
   pIORB->apt.pSGList->ppXferBuf =
              (ULONG)(ppDataSeg+(ULONG)((USHORT)&(pUnitCB->gen_buffer)));

   pIORB->apt.pSGList->XferBufLen = 0x16;    /* set transfer length */

   pCDB2->OpCode = SCSI_MODE_SENSE;
   pCDB2->page_code = 0x08;                  /* get Page #8 (address mode) */
   pCDB2->alloc_length = 16;                 /* set allocation length */

   /*
   ** Replace callback address with ours.
   */
   pFilter_workspace->completion_address = NotifyRead6;

   return CALL_ADD_AND_NOTIFY_FILTER;
}


/****************************************************************************
 *
 * FUNCTION NAME = NotifyRead6
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NotifyRead6 (PIORB_CDB pIORB)
{
   struct CDB_Read_6         FAR *pCDB1;
   struct ModeSelectParmList FAR *pCDBData1;
   PIORB_FLTWORK             pFilter_workspace;
   NPUNITCB                  pUnitCB;

   pCDB1 = (struct CDB_Read_6 FAR *) pIORB->apt.pControllerCmd;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;
   pUnitCB = pFilter_workspace->pUnitCB;

   /*
   ** Set flag to indicate that we have sent the Mode Sense request to
   ** the device.
   */
   pUnitCB->unit_flags |= MODE_SENSE;

   /*
   ** Look at Page #8 for the LBAMSF bit.
   */
   if ( pUnitCB->gen_buffer[14] & 0x01 )
      pUnitCB->unit_flags |= MSF_ADDR_MODE;

   /*
   ** Restore original physical data pointer for the Read request.
   */
   pIORB->apt.pSGList->ppXferBuf = pUnitCB->read_buffer;

   pIORB->apt.pSGList->XferBufLen = 0x800;    /* set transfer length */

   pCDB1->OpCode = 0x08;   /* set Read-6 command code */
   pCDB1->LBA_msb = 0;
   pCDB1->LBA.word = 0;
   pCDB1->transfer_length = 1;

   return CALL_ADD_AND_NOTIFY_CDM;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_Seek_6
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_Seek_6 (PIORB_CDB pIORB)
{
   /*
    * No conversion needed, pass along to ADD.
    */

   return CALL_ADD_AND_RET;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_ModeSelect
 *
 * DESCRIPTION   = This routine converts the Audio Control Mode Page to a Sony
 *                 Playback Control command.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_ModeSelect (PIORB_CDB pIORB)
{
   USHORT   i, rc;
   UCHAR    out_chan0, out_chan1, out_vol0, out_vol1;
   UCHAR    left_vol, right_vol;
   UCHAR    block_length_lsb, block_length_msb;

   pSONY_CDB_PlayControl      pCDB1;
   pSONY_DATA_PlayControl     pCDBData1;
   struct CDB_ModeSelect      FAR *pCDB2;
   struct ModeSelectParmList  FAR *pCDBData2;
   PIORB_FLTWORK              pFilter_workspace;

   pCDB1 = (pSONY_CDB_PlayControl) pIORB->apt.pControllerCmd;
   pCDB2 = (struct CDB_ModeSelect FAR *) pCDB1;
   pCDBData1 = (pSONY_DATA_PlayControl) pIORB->CDB_data;
   pCDBData2 = (struct ModeSelectParmList FAR *) pCDBData1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;

   *((PBYTE) pCDB2+1) = 0;    /* Byte 1 of the command block must be zero */

   /*
   ** Check for Block Descriptor Header.
   */
   if ( pCDBData2->ModeSelectHdr.block_descriptor_len == 8 )
   {
      pCDB2->parm_length = 10;
      pCDBData2->ModeSelectHdr.block_descriptor_len = 0;

      block_length_lsb = pCDBData2->Descriptors.BlockDescriptor.block_length_2;
      block_length_msb = pCDBData2->Descriptors.BlockDescriptor.block_length_1;

      /*
      ** Test for block size of 2048 or 2340 bytes.  Return an error for
      ** any other block size.
      */
      if ( !(block_length_msb == 0x08 && block_length_lsb == 0x00) &&
           !(block_length_msb == 0x09 && block_length_lsb == 0x24) )
      {
         return NOTIFY_CDM_WITH_ERROR;
      }

      /*
      ** Density Code and Block Count must be set to zero.
      */
      pCDBData2->Descriptors.BlockDescriptor.density_code   = 0;
      pCDBData2->Descriptors.BlockDescriptor.block_count_0  = 0;
      pCDBData2->Descriptors.BlockDescriptor.block_count_1  = 0;
      pCDBData2->Descriptors.BlockDescriptor.block_count_2  = 0;
   }

   rc = NOTIFY_CDM;

   /*
   ** Check for Audio Channel Control page.
   */
   if ( (pCDBData2->ModeSelectHdr.block_descriptor_len == 0) &&
        (pCDBData2->Descriptors.audio_control.page_code == PAGE_AUDIO_CONTROL) )
   {
      pIORB->apt.ControllerCmdLen = sizeof (SONY_CDB_PlayControl);
      pIORB->apt.pSGList->XferBufLen = sizeof (SONY_DATA_PlayControl);

      /*
      ** Setup command block
      */
      pCDB1->OpCode = SONY_PLAY_CONTROL;

      for (i=1;i<10;i++)
         *((PBYTE) pCDB1+i) = 0;

      pCDB1->parm_length_lsb = 18;

      out_chan0 = pCDBData2->Descriptors.audio_control.output0_select;
      out_vol0  = pCDBData2->Descriptors.audio_control.output0_volume;
      out_chan1 = pCDBData2->Descriptors.audio_control.output1_select;
      out_vol1  = pCDBData2->Descriptors.audio_control.output1_volume;

      left_vol = right_vol = 0xFF;     /* set default volume to maximum */

      /*
      ** Check for volume control of zero and output channel of zero.
      */
      if ( out_chan0 == 0 || out_vol0 == PCS_MUTED )
         left_vol = 0;

      if ( out_chan1 == 0 || out_vol1 == PCS_MUTED )
         right_vol = 0;

      /*
      ** Check for audio channel redirection.
      */
      if ( out_chan0 != 0 && out_chan0 == 2 )
         left_vol = 0;

      if ( out_chan1 != 0 && out_chan1 == 1 )
         right_vol = 0;

      /*
      ** If audio channels are switched, continue to play in stereo.
      */
      if ( out_chan0 == 2 && out_chan1 == 1 )
         left_vol = right_vol = 0xFF;

      for (i=0;i<18;i++)
         *((PBYTE) pCDBData1+i) = 0;

      /*
      ** Setup Audio Control Data packet
      */
      for (i=0;i<18;i++)
         *((PBYTE) pCDBData1+i) = 0;

      pCDBData1->output0_select = 1;
      pCDBData1->output1_select = 2;

      pCDBData1->output0_volume = left_vol;
      pCDBData1->output1_volume = right_vol;

      /*
      ** Replace callback address with ours.
      */
      pFilter_workspace->completion_address = NotifyChannelControl;
      pFilter_workspace->flags |= FWF_SKIP_ERROR_CHECK;

      rc = CALL_ADD_AND_NOTIFY_FILTER;
   }

   return rc;
}


/****************************************************************************
 *
 * FUNCTION NAME = NotifyChannelControl
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NotifyChannelControl (PIORB_CDB pIORB)
{
   /*
   ** Clear any error conditions that may have occurred
   */
   pIORB->apt.iorbh.Status = IORB_DONE;

   return NOTIFY_CDM;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_StartStopUnit
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_StartStopUnit (PIORB_CDB pIORB)
{
   /*
   ** No conversion needed, pass along to ADD.
   */

   return CALL_ADD_AND_RET;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_PreventAllowRemoval
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_PreventAllowRemoval (PIORB_CDB pIORB)
{
   /*
   ** No conversion needed, pass along to ADD.
   */

   return CALL_ADD_AND_RET;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_Seek_10
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_Seek_10 (PIORB_CDB pIORB)
{
   /*
   ** No conversion needed, pass along to ADD.
   */

   return CALL_ADD_AND_RET;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_ReadSubChannel
 *
 * DESCRIPTION   = This routine converts the SCSI-2 Read Sub Channel command to
 *                 a Hitachi Audio Status command.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_ReadSubChannel (PIORB_CDB pIORB)
{
   USHORT   i, rc;

   pSONY_CDB_ReadSub          pCDB1;
   struct CDB_ReadSubChannel  FAR *pCDB2;
   PIORB_FLTWORK              pFilter_workspace;

   pCDB1 = (pSONY_CDB_ReadSub) pIORB->apt.pControllerCmd;
   pCDB2 = (struct CDB_ReadSubChannel FAR *) pCDB1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;

   if ( pCDB2->data_format == RSC_CURRENT_POSITION )
   {
      pIORB->apt.ControllerCmdLen = sizeof (SONY_CDB_ReadSub);
      pIORB->apt.pSGList->XferBufLen = sizeof (SONY_DATA_ReadSub);

      pCDB1->OpCode = SONY_READ_SUB;

      for (i=1;i<10;i++)
         *((PBYTE) pCDB1+i) = 0;

      pCDB1->Sub_Q = 1;               /* return Sub-Q channel info */
      pCDB1->alloc_length_lsb = 9;

      /*
      ** Replace callback address with ours.
      */
      pFilter_workspace->completion_address = NotifyReadSub;

      rc = CALL_ADD_AND_NOTIFY_FILTER;
   }
   else
   {
      rc = NOTIFY_CDM_WITH_ERROR;
   }

   return rc;
}

/****************************************************************************
 *
 * FUNCTION NAME = NotifyReadSub
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NotifyReadSub (PIORB_CDB pIORB)
{
   USHORT   i, rc;

   pSONY_CDB_PlayStatus    pCDB1;
   pSONY_DATA_ReadSub      pCDBData1;
   pSONY_DATA_PlayStatus   pCDBData2;
   PIORB_FLTWORK           pFilter_workspace;

   pCDB1 = (pSONY_CDB_PlayStatus) pIORB->apt.pControllerCmd;
   pCDBData1 = (pSONY_DATA_ReadSub) pIORB->CDB_data;
   pCDBData2 = (pSONY_DATA_PlayStatus) pCDBData1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;

   /*
   ** Save Sub-Channel information which will be overwritten by the
   ** Autio Status request.
   */
   for (i=0;i<10;i++)
      *((PBYTE) pCDBData1+i+20) = *((PBYTE) pCDBData1+i);

   /*
   ** If drive head is on a Data Track, we do not want to issue a
   ** Playback Status command.
   */
   if ( pCDBData1->control & 0x04 )
   {
      pCDBData2->audio_status = SONY_PLAY_COMPLETE;

      rc = NotifyPlayStatus (pIORB);
   }
   else
   {
      pIORB->apt.ControllerCmdLen = sizeof (SONY_CDB_PlayStatus);
      pIORB->apt.pSGList->XferBufLen = sizeof (SONY_DATA_PlayStatus);

      pCDB1->OpCode = SONY_PLAY_STATUS;

      for (i=1;i<10;i++)
         *((PBYTE) pCDB1+i) = 0;

      pCDB1->alloc_length_lsb = 18;

      /*
      ** Replace callback address with ours.
      */

      pFilter_workspace->completion_address = NotifyPlayStatus;

      rc = CALL_ADD_AND_RET;
   }

   return rc;
}


/****************************************************************************
 *
 * FUNCTION NAME = NotifyPlayStatus
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NotifyPlayStatus (PIORB_CDB pIORB)
{
   UCHAR    i, audio_status;
   UCHAR    control, track_number, index;

   union {
      ULONG dword;

      struct {
         UCHAR byte_0;
         UCHAR byte_1;
         UCHAR byte_2;
         UCHAR byte_3;
      } bytes;
   } hsg_addr;

   pSONY_DATA_PlayStatus      pCDBData1;
   pSONY_DATA_ReadSub         pCDBData2;
   struct SubChannel_Position FAR *pCDBData3;
   PIORB_FLTWORK              pFilter_workspace;
   NPUNITCB                   pUnitCB;

   pCDBData1 = (pSONY_DATA_PlayStatus) pIORB->CDB_data;
   pCDBData2 = (pSONY_DATA_ReadSub) pCDBData1;
   pCDBData3 = (struct SubChannel_Position FAR *) pCDBData1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;
   pUnitCB = pFilter_workspace->pUnitCB;

   /*
   ** convert Sony audio status byte to SCSI-2
   */
   switch (pCDBData1->audio_status)
   {
      case SONY_PLAY_IN_PROGRESS:
         audio_status = AS_PLAY_IN_PROGRESS;
         break;

      case SONY_PLAY_PAUSED:
         audio_status = AS_PLAY_PAUSED;
         break;

      case SONY_PLAY_COMPLETE:
         audio_status = AS_PLAY_COMPLETE;
         break;

      case SONY_PLAY_ERROR:
         audio_status = AS_PLAY_ERROR;
         break;
   }

   /*
   ** Restore Sub-Channel information
   */
   for (i=0;i<10;i++)
      *((PBYTE) pCDBData2+i) = *((PBYTE) pCDBData2+i+20);

   control = pCDBData2->control;
   track_number = pCDBData2->track_number;
   index = pCDBData2->index_number;

   if ( pUnitCB->unit_flags & MSF_ADDR_MODE )
   {
      pCDBData3->rel_address.redbook.min   = pCDBData2->relative_2;
      pCDBData3->rel_address.redbook.sec   = pCDBData2->relative_1;
      pCDBData3->rel_address.redbook.frame = pCDBData2->relative_0;
      pCDBData3->rel_address.redbook.zero  = 0;

      pCDBData3->abs_address.redbook.min   = pCDBData2->absolute_2;
      pCDBData3->abs_address.redbook.sec   = pCDBData2->absolute_1;
      pCDBData3->abs_address.redbook.frame = pCDBData2->absolute_0;
      pCDBData3->abs_address.redbook.zero  = 0;
   }
   else
   {
      /*
      ** Fill in Current Position. We must convert from LBA to MSF format.
      */
      hsg_addr.bytes.byte_0 = pCDBData2->relative_0;
      hsg_addr.bytes.byte_1 = pCDBData2->relative_1;
      hsg_addr.bytes.byte_2 = pCDBData2->relative_2;
      hsg_addr.bytes.byte_3 = 0;

      hsg_addr.dword = HSGtoRedBook (hsg_addr.dword);

      pCDBData3->rel_address.redbook.min   = hsg_addr.bytes.byte_2;
      pCDBData3->rel_address.redbook.sec   = hsg_addr.bytes.byte_1;
      pCDBData3->rel_address.redbook.frame = hsg_addr.bytes.byte_0;
      pCDBData3->rel_address.redbook.zero  = 0;

      hsg_addr.bytes.byte_0 = pCDBData2->absolute_0;
      hsg_addr.bytes.byte_1 = pCDBData2->absolute_1;
      hsg_addr.bytes.byte_2 = pCDBData2->absolute_2;
      hsg_addr.bytes.byte_3 = 0;

      hsg_addr.dword = HSGtoRedBook (hsg_addr.dword);

      pCDBData3->abs_address.redbook.min   = hsg_addr.bytes.byte_2;
      pCDBData3->abs_address.redbook.sec   = hsg_addr.bytes.byte_1;
      pCDBData3->abs_address.redbook.frame = hsg_addr.bytes.byte_0;
      pCDBData3->abs_address.redbook.zero  = 0;
   }

   pCDBData3->data_format_code = 1;
   pCDBData3->control = control;
   pCDBData3->ADR = ADR_CURRENT_POSITION;
   pCDBData3->track_number = track_number;
   pCDBData3->index_number = index;

   /*
   ** Fill in sub-channel header
   */
   pCDBData3->sub_channel_hdr.reserved_1 = 0;
   pCDBData3->sub_channel_hdr.audio_status = audio_status;
   pCDBData3->sub_channel_hdr.data_length.usbytes.byte_0 = 0;
   pCDBData3->sub_channel_hdr.data_length.usbytes.byte_1 =
                                       sizeof(struct SubChannel_Position) -
                                       sizeof(struct SubChannel_Hdr);

   return NOTIFY_CDM;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_ReadTOC
 *
 * DESCRIPTION   = This routine gets the requested information form the SCSI-2
 *                 Read TOC command and obtains the necessary data from the
 *                 Hitachi TOC data table.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_ReadTOC (PIORB_CDB pIORB)
{
   USHORT i, track_nbr;

   pSONY_CDB_ReadTOC    pCDB1;
   struct CDB_ReadTOC   FAR *pCDB2;
   PIORB_FLTWORK        pFilter_workspace;

   pCDB1 = (pSONY_CDB_ReadTOC) pIORB->apt.pControllerCmd;
   pCDB2 = (struct CDB_ReadTOC FAR *) pCDB1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;

   pIORB->apt.ControllerCmdLen = sizeof (SONY_CDB_ReadTOC);
   pIORB->apt.pSGList->XferBufLen = sizeof (SONY_DATA_TOC);

   pCDB1->OpCode = SONY_READ_TOC;

   track_nbr = pCDB2->starting_track;

   for (i=1;i<10;i++)
      *((PBYTE) pCDB1+i) = 0;

   pCDB1->track_number = track_nbr;
   pCDB1->alloc_length_lsb = 10;

   /*
   ** Replace callback address with ours.
   */
   pFilter_workspace->completion_address = NotifyReadTOC;

   return CALL_ADD_AND_NOTIFY_FILTER;
}


/****************************************************************************
 *
 * FUNCTION NAME = NotifyReadTOC
 *
 * DESCRIPTION   =
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NotifyReadTOC (PIORB_CDB pIORB)
{
   USHORT   i;

   union {
      ULONG dword;

      struct {
         UCHAR byte_0;
         UCHAR byte_1;
         UCHAR byte_2;
         UCHAR byte_3;
      } bytes;
   } hsg_addr;

   pSONY_DATA_TOC       pCDBData1;
   struct ReadTOC_Data  FAR *pCDBData2;
   PIORB_FLTWORK        pFilter_workspace;
   NPUNITCB             pUnitCB;

   pCDBData1 = (pSONY_DATA_TOC) pIORB->CDB_data;
   pCDBData2 = (struct ReadTOC_Data FAR *) pCDBData1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;
   pUnitCB = pFilter_workspace->pUnitCB;

   pCDBData2->toc_hdr.toc_datalength.usbytes.byte_0 = 0;
   pCDBData2->toc_hdr.toc_datalength.usbytes.byte_1 =
                                    sizeof (struct ReadTOC_Data) - 2;

   pCDBData2->toc_descriptor[0].control = pCDBData1->control;
   pCDBData2->toc_descriptor[0].ADR = ADR_CURRENT_POSITION;

   if ( pUnitCB->unit_flags & MSF_ADDR_MODE )
   {
      pCDBData2->toc_descriptor[0].abs_address.redbook.frame =
                                             pCDBData1->track_address_0;
      pCDBData2->toc_descriptor[0].abs_address.redbook.sec =
                                             pCDBData1->track_address_1;
      pCDBData2->toc_descriptor[0].abs_address.redbook.min =
                                             pCDBData1->track_address_2;
      pCDBData2->toc_descriptor[0].abs_address.redbook.zero = 0;
   }
   else
   {
      /*
      ** Fill in Track Starting Position.
      ** We must convert from LBA to MSF format.
      */
      hsg_addr.bytes.byte_0 = pCDBData1->track_address_0;
      hsg_addr.bytes.byte_1 = pCDBData1->track_address_1;
      hsg_addr.bytes.byte_2 = pCDBData1->track_address_2;
      hsg_addr.bytes.byte_3 = pCDBData1->track_address_3;

      hsg_addr.dword = HSGtoRedBook (hsg_addr.dword);

      pCDBData2->toc_descriptor[0].abs_address.redbook.min   =
                                             hsg_addr.bytes.byte_2;
      pCDBData2->toc_descriptor[0].abs_address.redbook.sec   =
                                             hsg_addr.bytes.byte_1;
      pCDBData2->toc_descriptor[0].abs_address.redbook.frame =
                                             hsg_addr.bytes.byte_0;
      pCDBData2->toc_descriptor[0].abs_address.redbook.zero  = 0;
   }

   pCDBData2->toc_descriptor[0].track_num = pCDBData1->track_number;

   pCDBData2->toc_descriptor[0].reserved_1 = 0;
   pCDBData2->toc_descriptor[0].reserved_3 = 0;

   return NOTIFY_CDM;

}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_ReadHeader
 *
 * DESCRIPTION   = Because the Hitachi drive does not support the Read Header
 *                 command or equevalent, we always return a density code of 1.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_ReadHeader (PIORB_CDB pIORB)
{
   USHORT   i;

   struct CDB_ReadHeader   FAR *pCDB1;
   struct ReadHeader_Data  FAR *pCDBData1;

   pCDB1 = (struct CDB_ReadHeader FAR *) pIORB->apt.pControllerCmd;
   pCDBData1 = (struct ReadHeader_Data FAR *) pIORB->CDB_data;

   pCDBData1->cdrom_data_mode = 1;

   for (i=1;i<8;i++)
      *((PBYTE) pCDB1+i) = 0;

   return NOTIFY_CDM;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_PlayAudio_MSF
 *
 * DESCRIPTION   = This routine changes the SCSI-2 Command Code to a Sony Play
 *                 Audio Command Code.  The remaining bytes of the command block
 *                 are the same.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_PlayAudio_MSF (PIORB_CDB pIORB)
{
   pSONY_CDB_PlayMSF  pCDB1;

   pCDB1 = (pSONY_CDB_PlayMSF) pIORB->apt.pControllerCmd;

   pCDB1->OpCode = SONY_PLAY_MSF;

   return CALL_ADD_AND_RET;
}


/****************************************************************************
 *
 * FUNCTION NAME = Filter_Pause_Resume
 *
 * DESCRIPTION   = This routine converts the SCSI-2 Pause/Resume CDB to a Sony
 *                 Pause request.
 *
 * INPUT         = pIORB - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT Filter_Pause_Resume (PIORB_CDB pIORB)
{
   UCHAR    i, rc;

   pSONY_CDB_Pause         pCDB1;
   struct CDB_PauseResume  FAR *pCDB2;
   PIORB_FLTWORK           pFilter_workspace;

   pCDB1 = (pSONY_CDB_Pause) pIORB->apt.pControllerCmd;
   pCDB2 = (struct CDB_PauseResume FAR *) pCDB1;
   pFilter_workspace = (PIORB_FLTWORK) pIORB->filter_workspace;

   /*
   ** We should only get a pause request.
   */
   if ( !(pCDB2->resume) )
   {
      /*
      ** Convert SCSI-2 Pause to Sony Pause command
      */
      pIORB->apt.ControllerCmdLen = sizeof (SONY_CDB_Pause);

      pCDB1->OpCode = SONY_PAUSE;

      for (i=1;i<10;i++)
         *((PBYTE) pCDB1+i) = 0;

      pCDB1->pause = 1;          /* set pause bit to indicate pause mode */

      rc = CALL_ADD_AND_RET;
   }
   else
   {
      rc = NOTIFY_CDM_WITH_ERROR;
   }

   return rc;
}
