/* SCCSID = "src/dev/usb/USBAUDIO/MMDDCMDS.C, usb, c.basedd 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  MMDDCMDS.C                                            */
/*                                                                            */
/*   DESCRIPTIVE NAME: Audio device driver IDC routines for MMPM/2            */
/*                                                                            */
/*                                                                            */
/*   FUNCTION:  Process IOCTLS received from the Amp Mixer and stream data    */
/*              to/from the stream handler.                                   */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:    StreamManagerIDCCall()                                  */
/*                                                                            */
/*                                                                            */
/*   EXTERNAL REFERENCES:	GetStreamEntry()           			               */
/*                         SetStreamTime()                                    */
/*                         ISOSendBuffer()                                    */
/*                         GetBuffSize()                                      */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer          Comment                             */
/*  ----    --------  ----------          -------                             */
/*          98/10/17  Vjacheslav Chibis   Original developer.                 */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/



//****************************************************************************
//                     I N C L U D E S
//****************************************************************************

#define         INCL_NOPMAPI
#define         INCL_BASE
#define         INCL_DOS
#define         INCL_DOSDEVICES
#define         INCL_ERRORS

#include        "usbaud.h"
#include        "audsubs.h"
#include        "cdevhlp.h"


//**************************************************
//      IDC MMPM/2 routines
//**************************************************

RC      GetStreamEntry(PSTREAM FAR *, HSTREAM);
/*****************************************************************************
*                       E X T E R N S
******************************************************************************/
extern  GLOBAL GlobalTable;
extern  ULONG   gDevOperation;    // Holds operation that we are prepared for
extern  PROTOCOLTABLE ProtocolTable[NPROTOCOLS];
extern  ULONG   trk;

/*****************************************************************************
*                          C O D E
******************************************************************************/


/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:              DDCmdSetup
*
* DESCRIPTIVE NAME:     Sets up the audio device
*
* NOTES:        Match hStream parameter that Stream Handler will pass.
*               Ensure that the PDD is set up with the
*               correct DSP module, using the operation field
                of the structure matching hStream.
*
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT_ERROR:   ERROR_INVALID_STREAM
*               ERROR_INVALID_REQUEST
*               ERROR_INVALID_FUNCTION
*
* INPUTS:
*               ulFunction              DDCMD_SETUP
*               hStream                 Stream handle to set up
*               pSetupParm              Not used
*               ulSetupParmSize         Not used
*
*********************** END OF SPECIFICATIONS **********************/

RC      DDCmdSetup( PDDCMDSETUP pSetup )
{
   PSTREAM  pStream = GlobalTable.paStream;// pointer to Stream table
   RC       rc;

   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdSetup\r\n");
   #endif

   /*
   if( !gNoOfActiveAudioDevices||!gbActiveDeviceSupport )
      return(ERROR_INVALID_FUNCTION);
     */

   // Search stream table for matching stream handle //
   pStream = GlobalTable.paStream;
   if( rc = GetStreamEntry(&pStream, pSetup->hStream) )
      return(rc);       // can't find stream in table

   #ifdef DEBUG
   dsPrint2(DBG_CRITICAL, "USBAUD : DDCmdSetup : pStream=%lxh time=%lxh\r\n", (ULONG)pStream, *((ULONG FAR *)pSetup->pSetupParm));
   #endif

   /*
   ** Ensure that this stream operation matches the
   ** operation that the PDD is set to perform.
   ** Put another way.  If we were setup for an operation
   ** different than that which the stream handler is
   ** asking us to perform, then we are not prepared.
   ** In this case, return error to stream handler.
   **
   ** For example, if setting up to start a stream, make sure the
   ** stream we are about to start is the same stream that the
   ** amp mixer most recently inited.
   */

   // make sure stream is registered
   if( (pStream->ulFlags & STREAM_REGISTERED) == 0 )
      return(ERROR_INVALID_REQUEST);

   // Stream handler needs to adjust CumTime when doing a setup //
   SetStreamTime (pStream, *((ULONG FAR *)pSetup->pSetupParm));


   /*
   ** Make sure a stopped stream is "CLEAN" before
   ** starting again.
   */

   if( pStream->ulFlags & STREAM_STOPPED )
      pStream->usCurrIOBuffIndex = pStream->usLastIOBuffIndex = pStream->usFreeIOBuffIndex = 0;

   return(NO_ERROR);
}



/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:              DDCmdWrite
*
* DESCRIPTIVE NAME:     Write to the audio device using AUDIO_HPI
*                       (Playback Mode)
*
* FUNCTION:     Allows the Audio Stream Handlerto write buffers to
*               the audio device by passing buffers (IOBuffs) to
*               the physical hardware device.
*
* NOTES:        StreamNumber is set at the IDC entry point
*               This routine chains the incoming buffers from the stream handler
*               into Iobuf packets and updates the next index buffer
*               packet.  The current index is only updated during
*               interrupt time in the get_next_xbuf routine.
*
*               This routine is called at interrupt and non-interrupt time.
*
* ENTRY POINTS: DDCmdWrite()
*     LINKAGE: near
*
* INPUT:       Parm pointer
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT_ERROR:   ERROR_INVALID_STREAM
*               ERROR_INVALID_BLOCK
*
* INPUTS:
*               ulFunction              DDCMD_WRITE
*               hStream                 Stream handle
*               pBuffer                 Address of buffer to play data from
*               ulBufferSize            Size of pBuffer
*
*********************** END OF SPECIFICATIONS **********************/

RC  DDCmdWrite( PDDCMDREADWRITE pWrite )
{
   register USHORT  Current, Free, Previous;
   PSTREAM pStream = GlobalTable.paStream;
   RC      rc;


   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdWrite \r\n");
   #endif

   /* Validate data buffer pointer */
   if( pWrite->pBuffer==NULL )
      return(ERROR_INVALID_BLOCK) ;

   /* Search stream table for matching stream handle */
   if( rc = GetStreamEntry(&pStream, pWrite->hStream) )
      return(rc);


   #ifdef DEBUG
   dsPrint2(DBG_SPECIFIC, "USBAUD : DDCmdWrite : pStream=%lxh buffs=%d\r\n", (ULONG)pStream, pStream->ucBuffers);
   #endif


   /* Set indexes */
   Current = pStream->usCurrIOBuffIndex;
   Free = pStream->usFreeIOBuffIndex;

   /**************************************************
       Copy buffer pointer to "Free"

       pBufPhys - Physical address is sent from stream handler

       Buffer   - Physical address to virtual
       Tail     - beginning of buffer   (VIRTUAL)
       Head     - end of buffer         (VIRTUAL)
    **************************************************/

   //**************************************************
   // Fill buffer with data from pWrite - size of data,
   // address, usRunFlags, and place it in the queue.
   // If there's a previous buffer in the queue, the
   // new buffer will be filled in with the previous
   // buffer's values for ulposition. Other-
   // wise ulposition will be set to zero.
   //**************************************************

   pStream->IOBuff[Free].lSize      = pWrite->ulBufferSize;
   pStream->IOBuff[Free].lCount     = pWrite->ulBufferSize;
   pStream->IOBuff[Free].pBuffer    = pWrite->pBuffer;
   pStream->IOBuff[Free].pHead      = pStream->IOBuff[Free].pBuffer;
   pStream->IOBuff[Free].pTail      = pStream->IOBuff[Free].pHead + pWrite->ulBufferSize;
   pStream->IOBuff[Free].usRunFlags = pStream->IOBuff[Current].usRunFlags & (USHORT)~IOB_UNDERRUN;



   #ifdef DEBUG
   dsPrint2(DBG_CRITICAL, "USBAUD: DDCmdWrite index=%d pBuffer=%lxh\r\n", Free, (ULONG)pWrite->pBuffer);
   #endif


   pStream->IOBuff[Free].bEmpty = FALSE;

   if( Current != Free )
   {        /* There is a previous buffer */
      Previous=(!Free)?(MAXIOBUFFS-1):(Free-1);
      pStream->IOBuff[Previous].lCount = pWrite->ulBufferSize;
   }

   pStream->IOBuff[Free].ulPosition = 0L;

   /* Increment Free buffer index and check for wrap */
   if( (++Free == MAXIOBUFFS) )
      Free = 0 ;

   pStream->usFreeIOBuffIndex = Free;
   #ifdef DEBUG
   dsPrint2(DBG_SPECIFIC, "USBAUD: DDCmdWrite : BuffSize=%lxh pBuffer=%lxh\r\n", pWrite->ulBufferSize, (ULONG)pWrite->pBuffer);
   #endif

   pStream->ucBuffers++;

   if( !gNoOfActiveAudioDevices )
      if( pStream->ulFlags&STREAM_STREAMING )
      {
         #ifdef DEBUG
         dsPrint(DBG_CRITICAL, "USBAUD: DDCmdWrite: No audio devices attached.  Rejecting buffer\r\n");
         #endif
         return(ERROR_INVALID_FUNCTION);
      }
      else
      {
         #ifdef DEBUG
         dsPrint(DBG_CRITICAL, "USBAUD : DDCmdWrite : No devs&&not streaming\r\n");
         #endif


         if( gStartReject )
         {
            gStartReject=FALSE;

            #ifdef DEBUG
            dsPrint(DBG_CRITICAL, "USBAUD : DDCmdWrite : Start rejecting\r\n");
            #endif
            return(ERROR_INVALID_FUNCTION);
         }
         else
            return(NO_ERROR);

      }

   if( (pStream->ulFlags&STREAM_STREAMING) && gNoOfActiveAudioDevices && gbActiveDeviceSupport )
   {


      #ifdef DEBUG
      dsPrint4(DBG_SPECIFIC, "USBAUD: DDCmdWrite :  idx=%d Rem=%d Buffs=%d pBuff=%d\r\n", pStream->usCurrIOBuffIndex, pStream->ucRemained, pStream->ucBuffers,(ULONG)pStream->IOBuff[pStream->usCurrIOBuffIndex].pBuffer);
      #endif


      if( ++pStream->usCurrIOBuffIndex==MAXIOBUFFS )
         pStream->usCurrIOBuffIndex=0;


      //pStream->IOBuff[pStream->usCurrIOBuffIndex].bEmpty=FALSE;
      ISOSendBuffer(pStream, NO_BYPASS);
   }

   return( NO_ERROR );
}


/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:          DDCmdStatus
*
* DESCRIPTIVE NAME:     Query current ulposition (stream time)
*
* FUNCTION:     Allows the Audio stream handler to get the current
*               stream time ulposition value in milliseconds.
*
* NOTES:
*               This routine will return the current real-time
*               "stream-time" for the stream.
*
*               This routine is called at non-interrupt time.
*
* ENTRY POINTS: DDCmdStatus()
*     LINKAGE: near
*
* INPUT:
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT_ERROR:   ERROR_INVALID_STREAM
*
* INPUTS:
*               ulFunction              DDCMD_STATUS
*               hStream                 Stream handle
*
*********************** END OF SPECIFICATIONS **********************/

RC DDCmdStatus(PDDCMDSTATUS pStatus )
{

   PSTREAM pStream = GlobalTable.paStream;
   register RC rc;


   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdStatus\r\n");
   #endif

   if( rc = GetStreamEntry(&pStream, pStatus->hStream) )
   {
      #ifdef DEBUG
      dsPrint1(DBG_SPECIFIC, "USBAUD: DDCMDSTATUS error rc=%lx\r\n", rc);
      #endif
      return(rc);
   }

   #ifdef DEBUG
   dsPrint1(DBG_CRITICAL, "USBAUD : DDCmdStatus : pStream=%lxh\r\n", (ULONG)pStream);
   #endif

   GetStreamTime (pStream); // Get the current ulposition //

   /*  Return far pointer containing real-time position to stream handler */

   pStatus->ulStatusSize = sizeof(pStream->ulCumTime);
   pStatus->pStatus = &pStream->ulCumTime;

   #ifdef DEBUG
   dsPrint(DBG_DETAILED, "USBAUD : DDCmdStatus exit NO_ERROR\r\n");
   #endif
   return( NO_ERROR );
}


/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:          DDCmdControl
*
* DESCRIPTIVE NAME:
*
* FUNCTION:     Maps the AUDIO_CONTROL IDC commands to their IOCTL
*               equilivent.
*
* NOTES:    Since the entire routine uses the AUDIO_HPI calls,
*           Just set the current buffer's usRunFlags to the right command
*           Command from stream handler comes in pControl->ulCmd.
*           This routine sets flags, IOBuff and pStream structures.
*           These will be referenced at interrupt time for command execution.
*           For some operations, we will call stream handler directly.
*
* ENTRY POINTS: DDCmdControl()
*     LINKAGE: near
*
* INPUT:       Parm pointer
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT_ERROR:   ERROR_INVALID_FUNCTION
*               ERROR_STREAM_NOT_STARTED
*               ERROR_INVALID_SEQUENCE
*               ERROR_INVALID_STREAM
*               ERROR_TOO_MANY_EVENTS
*
* INPUTS:
*               ulFunction              DDCMD_CONTROL
*               hStream                 Stream handle
*               ulCmd                   Specific control command
*               pParm                   Not used
*               ulParmSize              Not used
*
*********************** END OF SPECIFICATIONS **********************/

RC  DDCmdControl(PDDCMDCONTROL pControl)
{
   register USHORT   Current, i;
   register RC       rc;
   PSTREAM  pStream = GlobalTable.paStream;
   ULONG    ulCuePoint;

   #ifdef DEBUG
   dsPrint1(DBG_SPECIFIC, "USBAUD: DDCmdControl entry Cmd=%lxh\r\n", pControl->ulCmd);
   #endif

   if( rc = GetStreamEntry(&pStream, pControl->hStream) )
      return(rc);


   #ifdef DEBUG
   dsPrint1(DBG_SPECIFIC, "USBAUD : DDCmdControl : pStream=%lxh\r\n", (ULONG)pStream);
   #endif

   Current = pStream->usCurrIOBuffIndex;

   /* Perform specific DDCMD_CONTROL function */

   switch( pControl->ulCmd )
   {
      case    DDCMD_START:

         if( !gNoOfActiveAudioDevices ||!gbActiveDeviceSupport )
         {
            #ifdef DEBUG
            dsPrint(DBG_CRITICAL, "USBAUD: DDCmdControl: Start : ERROR : Device not ready!\r\n");
            #endif


            gStartReject=TRUE;
            if( pStream->ucBuffers )
            {
               pStream->usLastIOBuffIndex=pStream->usCurrIOBuffIndex;
               if( ++pStream->usCurrIOBuffIndex==MAXIOBUFFS )
                  pStream->usCurrIOBuffIndex=0;

               ReportFreeBuffer(pStream);
            }

            if( pStream->ucBuffers )
            {
               pStream->usLastIOBuffIndex=pStream->usCurrIOBuffIndex;
               if( ++pStream->usCurrIOBuffIndex==MAXIOBUFFS )
                  pStream->usCurrIOBuffIndex=0;
               ReportFreeBuffer(pStream);
            }


            return(NO_ERROR);
         }

         #ifdef DEBUG
         dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdStart\r\n");
         #endif

         /* ... PAUSE - START ... is an invalid sequence */
         if( pStream->IOBuff[Current].usRunFlags & IOB_PAUSED )
            return(ERROR_INVALID_SEQUENCE);

         /* stream handler told us to start and we have enough
            full buffers, so send this buffer to the audio card. */

         pStream->ulFlags |= STREAM_STREAMING;
         pStream->ulFlags &= ~STREAM_STOPPED;

         /* Set all IOBuffs to START */
         for( i=0; i<MAXIOBUFFS; i++ )
            pStream->IOBuff[i].usRunFlags |= IOB_STARTED;

         if( gDevOperation == OPERATION_PLAY )
         {
            if( StartDevicePlay(pStream, TRUE, NO_BYPASS ) )
               return(ERROR_NOT_READY);
         }
         else
         {
            #ifdef DEBUG
            dsPrint1(DBG_SPECIFIC, "USBAUD : DDCmdControl : Operation=%d\r\n", gDevOperation);
            #endif
         } 

         break;


      case DDCMD_STOP:
      #ifdef DEBUG
         dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdStop\r\n");
      #endif
         while( TRUE )
         {
            if( GetNextBuffer(pStream)!=STATUS_DONE )
               break;
            ReportFreeBuffer(pStream);
         }


         // because we always write to the "next" buffer and play
         // from the "current" buffer, reset the indexes to point at the same packet.
         // So when a re-start comes in we will not play a null buffer. 


         Current = pStream->usCurrIOBuffIndex = pStream->usFreeIOBuffIndex = pStream->usLastIOBuffIndex = 0;
         pStream->ulFlags |= STREAM_STOPPED;
         pStream->ulFlags &= ~STREAM_STREAMING;
         pStream->ulFlags &= ~STREAM_PAUSED;


         pStream->ucBuffers = 0;

         /* Do this so that PDD does not return an old buffer
         to handler after handler received a STOP-DISCARD. */

         for( i=0; i<MAXIOBUFFS; i++ )
         {
            pStream->IOBuff[i].usRunFlags &=~ IOB_RUNNING;
            pStream->IOBuff[i].usRunFlags &=~ IOB_STARTED;
            pStream->IOBuff[i].usRunFlags &=~ IOB_PAUSED;
            pStream->IOBuff[i].usRunFlags |=  IOB_STOPPED;
            pStream->IOBuff[i].lSize       =  0L;
            pStream->IOBuff[i].pBuffer     =  NULL;
            pStream->IOBuff[i].lCount      =  0L;
            pStream->IOBuff[i].ulPosition  =  0L;
            pStream->IOBuff[i].pBuffer     =  NULL;
            pStream->IOBuff[i].bEmpty      =  TRUE;
         }

         // Always return stream time when stopping or pausing device. 
         pControl->pParm = GetStreamTime(pStream);
         pControl->ulParmSize = sizeof(pStream->ulCumTime);

         break ;


      case    DDCMD_PAUSE:
         #ifdef DEBUG
         dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdPause \r\n");
         #endif

         /* trying to PAUSE a non-started stream = error */
         if( !(pStream->IOBuff[Current].usRunFlags & IOB_STARTED) )
         {
            #ifdef DEBUG
            dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdPause : ERROR : ERROR_STREAM_NOT_STARTED \r\n");
            #endif

            return(ERROR_STREAM_NOT_STARTED);
         }

         pStream->IOBuff[Current].usRunFlags |= IOB_PAUSED;
         pStream->ulFlags |= STREAM_PAUSED;
         pStream->ulFlags &= ~STREAM_STREAMING;

         /* Always return stream time when stopping or pausing device. */
         pControl->pParm = GetStreamTime(pStream);
         pControl->ulParmSize = sizeof(pStream->ulCumTime);


         {
            CHAR FAR * pBuffer=pStream->IOBuff[pStream->usCurrIOBuffIndex].pBuffer;
            USHORT usProcBytes=0;
            if( gNoOfActiveAudioDevices )
               ISOCancel((CHAR FAR * FAR *)&pBuffer, (USHORT FAR *)&usProcBytes);

            #ifdef DEBUG
            dsPrint2(DBG_SPECIFIC, "USBAUD: DDCmdPause : pbuf=%lxh bytes=%xh\r\n", (ULONG)pBuffer, usProcBytes);
            #endif

            pStream->IOBuff[pStream->usCurrIOBuffIndex].pHead+= usProcBytes;
            pStream->IOBuff[pStream->usCurrIOBuffIndex].lSize-=usProcBytes;
         }

         break ;

      case    DDCMD_RESUME:
         #ifdef DEBUG
         dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdResume\r\n");
         #endif

         /* trying to RESUME a stoped/non-paused stream = error */
         if( !(pStream->IOBuff[Current].usRunFlags & IOB_STARTED) )
            return(ERROR_INVALID_SEQUENCE);


         if( !gNoOfActiveAudioDevices ||!gbActiveDeviceSupport )
         {
            #ifdef DEBUG
            dsPrint(DBG_CRITICAL, "USBAUD: DDCmdControl: Resume : ERROR : Device not ready!\r\n");
            #endif


            gStartReject=TRUE;
            if( pStream->ucBuffers )
            {
               pStream->usLastIOBuffIndex=pStream->usCurrIOBuffIndex;
               if( ++pStream->usCurrIOBuffIndex==MAXIOBUFFS )
                  pStream->usCurrIOBuffIndex=0;

               ReportFreeBuffer(pStream);
            }

            if( pStream->ucBuffers )
            {
               pStream->usLastIOBuffIndex=pStream->usCurrIOBuffIndex;
               if( ++pStream->usCurrIOBuffIndex==MAXIOBUFFS )
                  pStream->usCurrIOBuffIndex=0;
               ReportFreeBuffer(pStream);
            }

            return(NO_ERROR);
         }


         for( i=0; i<MAXIOBUFFS; i++ )
            pStream->IOBuff[i].usRunFlags &= ~IOB_PAUSED;

         pStream->ulFlags |= STREAM_STREAMING;
         pStream->ulFlags &= ~STREAM_PAUSED;

         if( !pStream->usCurrIOBuffIndex )
            pStream->usCurrIOBuffIndex=MAXIOBUFFS-1;
         else
            pStream->usCurrIOBuffIndex--;

         #ifdef DEBUG
         dsPrint2(DBG_CRITICAL, "USBAUD: DDCmdResume remained=%d cur=%d\r\n", pStream->ucBuffers, pStream->usCurrIOBuffIndex);
         {
            USHORT i=0;
            for( ;i<MAXIOBUFFS;i++ )
               dsPrint2(DBG_CRITICAL, "USBAUD: idx=%d empty=%d\r\n", i, pStream->IOBuff[i].bEmpty);
         }
         #endif

         if( gDevOperation == OPERATION_PLAY )
            if( StartDevicePlay(pStream, TRUE, NO_BYPASS) )
               return(ERROR_NOT_READY);

         break ;

      case    DDCMD_ENABLE_EVENT:

         if( !gbTimerIntHooked )
            return(ERROR_INVALID_REQUEST);

         /* Create an event with specific cuepoint.This PDD will detect
            the cuepoint and report its occurance to the stream handler via SHD_REPORT_EVENT */

         ulCuePoint = *((ULONG FAR *)pControl->pParm);

         rc = EnqueueEvent (pControl->hEvent, ulCuePoint, pStream);
         #ifdef DEBUG
         dsPrint1(DBG_SPECIFIC, "USBAUD: ENABLE_EVENT ulCuePoint=%lxh\r\n", ulCuePoint);
         #endif
         return(rc);

         break ;

      case    DDCMD_DISABLE_EVENT:
         #ifdef DEBUG
         dsPrint(DBG_SPECIFIC, "USBAUD: DISABLE_EVENT\r\n");
         #endif
         if( !gbTimerIntHooked )
            return(ERROR_INVALID_REQUEST);
         return(DequeueEvent (pControl->hEvent, pStream));
         break ;

      default:  
         #ifdef DEBUG
         dsPrint(DBG_CRITICAL, "USBAUD: DDCMDCONTROL error ERROR_INVALID_FUNCTION\r\n");
         #endif
         return(ERROR_INVALID_FUNCTION) ;
   }     /* switch */

   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdControl exit NO_ERROR\r\n");
   #endif

   return( NO_ERROR );
}


/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:          DDCmdRegister
*
* DESCRIPTIVE NAME: Register stream with this PDD
*
* FUNCTION: Initialize stream.
*
* NOTES:    Will receive the handler ID, handle of stream instance,
*           address of entry point from stream handler.
*           Set waiting flag (waiting for data)
*           Set usRunFlags = 0
*           Set No_circular_buffer flag (0x80)
*           Set protocol info for stream handler
*           Set Address type to Physical
*
* ENTRY POINTS:DDCmdRegister()
*     LINKAGE: near
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT-ERROR:   ERROR_INVALID_STREAM
*               ERROR_HNDLR_REGISTERED
*               ERROR_INVALID_FUNCTION
*               ERROR_INVALID_SPCBKEY
*
* INPUTS:
*               ulFunction              DDCMD_REG_STREAM
*               hStream                 Stream handle
*               ulSysFileNum            Device handle so PDD can map device instance to hStream
*               pSHDEntryPoint          Stream handler entry point
*               ulStreamOperation       Record (PRODUCE) or Play (CONSUME)
*               spcbkey                 Protocol info which determines outputs
*
* OUTPUTS:
*               ulBufSize               Buffer size in bytes for SPCB
*               ulNumBufs               # of buffers for SPCB
*               ulAddressType           Address type of data buffer
*               ulBytesPerUnit          Bytes per unit
*               mmtimePerUnit           MMTIME per unit
*
*********************** END OF SPECIFICATIONS **********************/

RC  DDCmdRegister(PDDCMDREGISTER pRegister)
{

   register    USHORT i;
   register    RC rc;
   PSTREAM     pStream;
   BOOL        bFound;


   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdRegister\r\n");
   #endif


   if( !gNoOfActiveAudioDevices )
      return(ERROR_INITIALIZATION);

   {
      PSTREAM paStream=GlobalTable.paStream;

      if( ((paStream->ulSamplingFreq> (gulSamplingFreq+gulSamplingFreq/10) )||
      (paStream->ulSamplingFreq<(gulSamplingFreq-gulSamplingFreq/10)))||
      (gulBitsPerSample!=paStream->ulBitsPerSample)||(gusChannels!=paStream->usChannels) )
      {

      #ifdef DEBUG
         dsPrint3(DBG_CRITICAL, "USBAUD: DDCmdRegister: unsupported data format - freq=%ld bits=%ld chan=%d\r\n",  
         paStream->ulSamplingFreq,
         paStream->ulBitsPerSample,
         paStream->usChannels);
         dsPrint4(DBG_CRITICAL, "USBAUD: DDCmdRegister: expected data format - freq=[%ld..%ld] bits=%ld chan=%d\r\n",  
         gulSamplingFreq-gulSamplingFreq/10,
         gulSamplingFreq+gulSamplingFreq/10,
         gulBitsPerSample,
         gusChannels);
      #endif
         return(ERROR_INITIALIZATION);
      }
      paStream->ulSamplingFreq=gulSamplingFreq;
   }

   gbActiveDeviceSupport=TRUE;

   if( pRegister->hStream == -1 )
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister pRegister->hStream == -1 \r\n");
   #endif

      return(ERROR_INVALID_STREAM) ;    /*   Stream handle invalid   */
   }


   if( pRegister->ulSysFileNum == DATATYPE_NULL )
   {

      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister pRegister->ulSysFileNum == DATATYPE_NULL \r\n");
      #endif
      return(ERROR_INITIALIZATION);
   }


   if( (pRegister->ulStreamOperation != STREAM_OPERATION_CONSUME) &&
   (pRegister->ulStreamOperation != STREAM_OPERATION_PRODUCE) )
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister ERROR_INVALID_FUNCTION\r\n");
      #endif
      return(ERROR_INVALID_FUNCTION);
   }


   //******************************************
   // Do protocol table lookup, using DataType,
   // DataSubType as keys and return ulBufSize,
   // ulNumBufs, ulAddressType, mmtimePerUnit;
   // Verify requested stream characteristics
   // match our capabilities.
   //******************************************
   for( i=0; i<NPROTOCOLS; i++ )
      if( ProtocolTable[i].ulDataType == pRegister->spcbkey.ulDataType )
         if( ProtocolTable[i].ulDataSubType == pRegister->spcbkey.ulDataSubType )
            /* We found a match for data type, data sub type */
            break ;

         /* No match found */
   if( i==NPROTOCOLS )
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister ERROR_INVALID_SPCBKEY \r\n");
      #endif
      return(ERROR_INVALID_SPCBKEY) ;
   }

   /* Match found:  Pass back protocol ("stream characteristics") to stream handler. */

   else
   {                                  /* Match found */
      #ifdef DEBUG
      dsPrint1(DBG_SPECIFIC, "USBAUD: pRegister->ulBufSize=%lxh\r\n", pRegister->ulBufSize);
      #endif
      //****************************************************
      // Bytes per unit =
      //
      //  Samples     Channels     Bits      Byte     Sec
      //  -------  *           *  ------  *  ---- *  ------
      //    Sec                   Sample     Bits    MMTIME
      //****************************************************

      pRegister->ulNumBufs= 0L;//ProtocolTable[i].ulNumBufs;
      #ifdef DEBUG
      dsPrint1(DBG_SPECIFIC, "USBAUD: ulBytesPerUnit=%lxh \r\n", pRegister->ulBytesPerUnit);
      dsPrint2(DBG_SPECIFIC, "USBAUD: Dtype=%lxh DSType=%lxh\r\n", ProtocolTable[i].ulDataType, ProtocolTable[i].ulDataSubType);
      dsPrint2(DBG_SPECIFIC, "USBAUD: ulBufSize=%lxh ulNumBufs=%lxh\r\n", pRegister->ulBufSize, pRegister->ulNumBufs);
      #endif

   }

   #ifdef DEBUG
   dsPrint3(DBG_SPECIFIC,"USBAUD: DDCMDREGISTER : SRate=%d Bits=%d Chan=%d\r\n",
   (USHORT)pRegister->AudioMode.lSRate,
   (USHORT)pRegister->AudioMode.lBitsPerSRate,
   (USHORT)pRegister->AudioMode.sChannels
   );
   #endif                       
   /* Tell stream handler what type of data buffer pointer to reference
    You may choose PHYSICAL, LINEAR, or  VIRTUAL */

   pRegister->ulAddressType = ADDRESS_TYPE_VIRTUAL ;

   bFound = FALSE;

   if( trk == pRegister->ulSysFileNum )
      bFound = TRUE;

   if( !bFound )
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister ERROR_INVALID_STREAM \r\n");
      #endif
      return(ERROR_INVALID_STREAM);
   }

   /* Initialize pStream to point at the first entry for track trk */
   pStream = GlobalTable.paStream;
   if( !(rc = GetStreamEntry(&pStream, pRegister->hStream)) )
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister ERROR_HNDLR_REGISTERED \r\n");
      #endif
      return(ERROR_HNDLR_REGISTERED);
   }

   pStream = GlobalTable.paStream;      // no match, so create stream

   i = 0;

   while( pStream->hStream != -1 )
   {                     // find an empty stream entry
      if( ++i >= GlobalTable.usMaxNumStreams )
      {
         #ifdef DEBUG
         dsPrint(DBG_CRITICAL, "USBAUD: DDCmdRegister ERROR_STREAM_CREATION \r\n");
         #endif
         return(ERROR_STREAM_CREATION);
      }

      pStream++;
   }

   /* Found empty stream entry, so use it.  Fill Stream structure */
   for( i=0; i<MAXIOBUFFS; i++ )
   {
      pStream->IOBuff[i].bEmpty     =  TRUE;
      pStream->IOBuff[i].lSize      =  0L;
      pStream->IOBuff[i].pHead      =  NULL;
      pStream->IOBuff[i].pTail      =  NULL;
      pStream->IOBuff[i].lCount     =  0L;
      pStream->IOBuff[i].ulPosition =  0L;
      pStream->IOBuff[i].usRunFlags =  IOB_CHAIN_BUFFERS;
      pStream->IOBuff[i].pBuffer    =  NULL;

   }

   /* Save register info in structure */
   pStream->usMode = GlobalTable.paStream->usMode;
   pStream->hStream           = pRegister->hStream; 
   pStream->ulFlags           = STREAM_REGISTERED;
   pStream->ulOperation       = pRegister->ulStreamOperation;
   pStream->ulSysFileNum      = trk;
   pStream->ulCumTime         = 0L;
   pStream->usTrackNum        = 0;
   pStream->usCurrIOBuffIndex = 0;
   pStream->usFreeIOBuffIndex = 0;
   pStream->usLastIOBuffIndex = 0;
   pStream->ucRemained  = 0;
   pStream->ucBuffers   = 0;
   pStream->ulSamplingFreq    = gulSamplingFreq;
   pStream->ulBitsPerSample   = gulBitsPerSample;    
   pStream->usChannels        = gusChannels;

   if( pStream->usMode==MIDI || pStream->usMode==DATATYPE_MIDI )
   {
      pStream->usFrameSize  =  0x10;
      pRegister->ulBufSize  =  0x200;
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD: Attemping to play MIDI data\r\n");
      #endif
   }
   else
   {
      pStream->usFrameSize = (USHORT)((pStream->ulSamplingFreq*pStream->ulBitsPerSample*pStream->usChannels) /8000); 
      pRegister->ulBufSize = GetBuffSize(pStream);
      #ifdef DEBUG
      dsPrint1(DBG_SPECIFIC, "USBAUD: DDCmdRegister: Buf Size =%lxh\r\n",pRegister->ulBufSize);
      #endif
   }
   (PSHDFN)pStream->ADSHEntry = pRegister->pSHDEntryPoint;


   #ifdef DEBUG
   dsPrint1(DBG_CRITICAL, "USBAUD : DDCmdRegister : exit NO_ERROR pStream=%lxh\r\n", (ULONG)pStream);
   #endif

   return( NO_ERROR );
}

/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME:          DDCmdDeRegister
*
* DESCRIPTIVE NAME: Deregister this stream
*
* FUNCTION:     Remove stream instance from this PDD
*
* NOTES:        Done by setting table handle to value -1
*
* ENTRY POINTS:
*     LINKAGE:
*
* EXIT-NORMAL:  NO_ERROR
*
* EXIT_ERROR:   ERROR_INVALID_STREAM
*
* INPUTS:
*               ulFunction              DDCMD_DEREG_STREAM
*               hStream                 Stream handle
*
*********************** END OF SPECIFICATIONS **********************/

RC  DDCmdDeRegister(PDDCMDDEREGISTER pDeRegister)
{
   register USHORT  i;
   register RC rc;
   PSTREAM pStream = GlobalTable.paStream;


   #ifdef DEBUG
   dsPrint(DBG_SPECIFIC, "USBAUD: DDCmdDeRegister\r\n");
   #endif
   /* Check table to see if stream is registered and a valid handle */

   if( rc = GetStreamEntry(&pStream, pDeRegister->hStream) )
   {
   #ifdef DEBUG
      dsPrint1(DBG_CRITICAL, "USBAUD: DDCMDDEREGISTER error rc=%lx\r\n", rc);
   #endif
      return(rc);

   }

   #ifdef DEBUG
   dsPrint1(DBG_CRITICAL, "USBAUD : DDCmdDeRegister : pStream=%lxh\r\n", (ULONG)pStream);
   #endif

   /* De-activate this stream and clear all flags */
   pStream->hStream = -1; // make stream available
   pStream->ulFlags = 0L;  // clear flags

   for( i=0; i<MAXIOBUFFS; i++ )
      pStream->IOBuff[i].usRunFlags = 0;

   #ifdef DEBUG
   dsPrint(DBG_IRQFLOW, "USBAUD: DDCmdDeRegister exit NO_ERROR\r\n");
   #endif

   return( NO_ERROR );
}

//*******************************************************
// IDC Command function jump table                      *
//*******************************************************
RC   (*DDCMDFuncs[])(PVOID pCommon) = {
   DDCmdSetup,                         // 0
   0L,       /* read */                // 1
   DDCmdWrite,                         // 2
   DDCmdStatus,                        // 3
   DDCmdControl,                       // 4
   DDCmdRegister,                      // 5
   DDCmdDeRegister                     // 6
};
USHORT  MaxDDCMDFuncs = sizeof(DDCMDFuncs)/sizeof(USHORT);

RC StreamManagerIDCCall(PDDCMDCOMMON pSMRP)
/*    This function services requests from the audio stream handler. The parameter 'pSMRP' is 
        a pointer to a buffer containing information specific to the idc request. */
{
   return(pSMRP==NULL?(ULONG)ERROR_INVALID_BLOCK:(((pSMRP->ulFunction > (ULONG)MaxDDCMDFuncs)||(pSMRP->ulFunction == DDCMD_READ))?
   (ULONG)ERROR_INVALID_FUNCTION:DDCMDFuncs[pSMRP->ulFunction](pSMRP)));
}
