/**
  ******************************************************************************
  * @file    usbh_audio.c
  * @author  MCD Application Team
  * @version V3.2.2
  * @date    07-July-2015
  * @brief   This file is the AC Layer Handlers for USB Host AC class. 
  *
  * @verbatim
  *      
  *          ===================================================================      
  *                                AUDIO Class  Description
  *          ===================================================================
  *           This driver manages the Audio Class 1.0 following the "USB Device 
  *           Class Definition for Audio Devices V1.0 Mar 18, 98".
  *   
  *  @endverbatim
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include "usbh_audio.h"

/** @addtogroup USBH_LIB
  * @{
  */

/** @addtogroup USBH_CLASS
  * @{
  */

/** @addtogroup USBH_AUDIO_CLASS
  * @{
  */

/** @defgroup USBH_AUDIO_CORE 
  * @brief    This file includes HID Layer Handlers for USB Host HID class.
  * @{
  */ 

/** @defgroup USBH_AUDIO_CORE_Private_TypesDefinitions
  * @{
  */ 
/**
  * @}
  */ 


/** @defgroup USBH_AUDIO_CORE_Private_Defines
  * @{
  */ 

/**
  * @}
  */ 


/** @defgroup USBH_AUDIO_CORE_Private_Macros
  * @{
  */ 
/**
  * @}
  */ 


/** @defgroup USBH_AUDIO_CORE_Private_Variables
  * @{
  */

/**
  * @}
  */ 


/** @defgroup USBH_AUDIO_CORE_Private_FunctionPrototypes
  * @{
  */ 

static USBH_StatusTypeDef USBH_AUDIO_InterfaceInit  (USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_InterfaceDeInit  (USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_Process(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_SOFProcess(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_ClassRequest(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_CSRequest(USBH_HandleTypeDef *phost, 
                                               uint8_t feature, 
                                               uint8_t channel);

static USBH_StatusTypeDef USBH_AUDIO_HandleCSRequest(USBH_HandleTypeDef *phost);

static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingIN(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingOUT(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_FindHIDControl(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_ParseCSDescriptors(USBH_HandleTypeDef *phost);

static USBH_StatusTypeDef USBH_AUDIO_BuildHeadphonePath(USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_BuildMicrophonePath(USBH_HandleTypeDef *phost);
int32_t USBH_AUDIO_FindLinkedUnitIN(USBH_HandleTypeDef *phost, uint8_t UnitID);
int32_t USBH_AUDIO_FindLinkedUnitOUT(USBH_HandleTypeDef *phost, uint8_t UnitID);



static USBH_StatusTypeDef ParseCSDescriptors(AUDIO_ClassSpecificDescTypedef *class_desc, 
                                      uint8_t ac_subclass, 
                                      uint8_t *pdesc);


static USBH_StatusTypeDef USBH_AUDIO_Transmit (USBH_HandleTypeDef *phost);


static USBH_StatusTypeDef USBH_AC_SetCur(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length);

static USBH_StatusTypeDef USBH_AC_GetCur(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length);

static USBH_StatusTypeDef USBH_AC_GetMin(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length);

static USBH_StatusTypeDef USBH_AC_GetMax(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length);

static USBH_StatusTypeDef USBH_AC_GetRes(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length);

static USBH_StatusTypeDef USBH_AUDIO_SetEndpointControls(USBH_HandleTypeDef *phost,
                                               uint8_t  Ep,
                                               uint8_t *buff);

static USBH_StatusTypeDef AUDIO_SetVolume (USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel, uint16_t volume);

static USBH_StatusTypeDef USBH_AUDIO_InputStream (USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_OutputStream (USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_Control (USBH_HandleTypeDef *phost);
static USBH_StatusTypeDef USBH_AUDIO_SetControlAttribute (USBH_HandleTypeDef *phost, uint8_t attrib);
static int32_t USBH_AUDIO_FindLinkedUnit(USBH_HandleTypeDef *phost, uint8_t UnitID);

USBH_ClassTypeDef  AUDIO_Class = 
{
  "AUDIO",
  AC_CLASS,  
  USBH_AUDIO_InterfaceInit,
  USBH_AUDIO_InterfaceDeInit,
  USBH_AUDIO_ClassRequest,
  USBH_AUDIO_Process,
  USBH_AUDIO_SOFProcess,
  NULL,
};

/**
  * @}
  */ 

/** @defgroup USBH_AUDIO_CORE_Private_Functions
  * @{
  */ 

/**
  * @brief  USBH_AUDIO_InterfaceInit 
  *         The function init the Audio class.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_InterfaceInit (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_FAIL ;
  USBH_StatusTypeDef out_status, in_status ;    
  AUDIO_HandleTypeDef *AUDIO_Handle;
  uint8_t  interface, index;
  uint16_t ep_size_out = 0;
  uint16_t ep_size_in = 0;  
  
  interface = USBH_FindInterface(phost, AC_CLASS, USB_SUBCLASS_AUDIOCONTROL, 0x00);
  
  if(interface == 0xFF) /* Not Valid Interface */
  {
    USBH_DbgLog ("Cannot Find the interface for %s class.", phost->pActiveClass->Name);
    status = USBH_FAIL;      
  }
  else
  {
    
    
    phost->pActiveClass->pData = (AUDIO_HandleTypeDef *)USBH_malloc (sizeof(AUDIO_HandleTypeDef));
    AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; 
    USBH_memset(AUDIO_Handle, 0, sizeof(AUDIO_HandleTypeDef));
    
    
    /* 1st Step:  Find Audio Interfaces */
    out_status = USBH_AUDIO_FindAudioStreamingIN (phost);
    
    in_status = USBH_AUDIO_FindAudioStreamingOUT(phost);
    
    if((out_status == USBH_FAIL) && (in_status == USBH_FAIL))
    {
      USBH_DbgLog ("%s class configuration not supported.", phost->pActiveClass->Name);
    }
    else
    {
      /* 2nd Step:  Select Audio Streaming Interfaces with largest endpoint size : default behavior*/ 
      for (index = 0; index < AUDIO_MAX_AUDIO_STD_INTERFACE; index ++)
      {
        if( AUDIO_Handle->stream_out[index].valid == 1)
        {
          if(ep_size_out < AUDIO_Handle->stream_out[index].EpSize)
          {
            ep_size_out = AUDIO_Handle->stream_out[index].EpSize;
            AUDIO_Handle->headphone.interface = AUDIO_Handle->stream_out[index].interface;
            AUDIO_Handle->headphone.AltSettings = AUDIO_Handle->stream_out[index].AltSettings;           
            AUDIO_Handle->headphone.Ep = AUDIO_Handle->stream_out[index].Ep;
            AUDIO_Handle->headphone.EpSize = AUDIO_Handle->stream_out[index].EpSize; 
            AUDIO_Handle->headphone.Poll = AUDIO_Handle->stream_out[index].Poll;
            AUDIO_Handle->headphone.supported = 1;  
          }
        }
        
        if( AUDIO_Handle->stream_in[index].valid == 1)
        {
          if(ep_size_in < AUDIO_Handle->stream_in[index].EpSize)
          {
            ep_size_in = AUDIO_Handle->stream_in[index].EpSize;
            AUDIO_Handle->microphone.interface = AUDIO_Handle->stream_in[index].interface;
            AUDIO_Handle->microphone.AltSettings = AUDIO_Handle->stream_in[index].AltSettings;                
            AUDIO_Handle->microphone.Ep = AUDIO_Handle->stream_in[index].Ep;
            AUDIO_Handle->microphone.EpSize = AUDIO_Handle->stream_in[index].EpSize;
            AUDIO_Handle->microphone.Poll = AUDIO_Handle->stream_out[index].Poll;         
            AUDIO_Handle->microphone.supported = 1;       
          }
        }
      }
      
      if(USBH_AUDIO_FindHIDControl(phost) == USBH_OK)
      {
        AUDIO_Handle->control.supported = 1;
      }
      
      /* 3rd Step:  Find and Parse Audio interfaces */ 
      USBH_AUDIO_ParseCSDescriptors (phost);
     
      
      /* 4th Step:  Open the Audio streaming pipes*/ 
      if(AUDIO_Handle->headphone.supported == 1)
      {
        USBH_AUDIO_BuildHeadphonePath (phost);
         
        AUDIO_Handle->headphone.Pipe  = USBH_AllocPipe(phost, AUDIO_Handle->headphone.Ep);
        
        /* Open pipe for IN endpoint */
        USBH_OpenPipe  (phost,
                        AUDIO_Handle->headphone.Pipe,
                        AUDIO_Handle->headphone.Ep,
                        phost->device.address,
                        phost->device.speed,
                        USB_EP_TYPE_ISOC,
                        AUDIO_Handle->headphone.EpSize); 
        
        USBH_LL_SetToggle (phost,  AUDIO_Handle->headphone.Pipe, 0);          
        
      }
      
      if(AUDIO_Handle->microphone.supported == 1)
      {
        USBH_AUDIO_BuildMicrophonePath (phost);        
        AUDIO_Handle->microphone.Pipe  = USBH_AllocPipe(phost, AUDIO_Handle->microphone.Ep);
        
        /* Open pipe for IN endpoint */
        USBH_OpenPipe  (phost,
                        AUDIO_Handle->microphone.Pipe,
                        AUDIO_Handle->microphone.Ep,
                        phost->device.address,
                        phost->device.speed,
                        USB_EP_TYPE_ISOC,
                        AUDIO_Handle->microphone.EpSize); 
        
        USBH_LL_SetToggle (phost,  AUDIO_Handle->microphone.Pipe, 0);  
      }
      
      if(AUDIO_Handle->control.supported == 1)
      {     
        AUDIO_Handle->control.Pipe  = USBH_AllocPipe(phost, AUDIO_Handle->control.Ep);
        
        /* Open pipe for IN endpoint */
        USBH_OpenPipe  (phost,
                        AUDIO_Handle->control.Pipe,
                        AUDIO_Handle->control.Ep,
                        phost->device.address,
                        phost->device.speed,
                        USB_EP_TYPE_INTR,
                        AUDIO_Handle->control.EpSize); 
        
        USBH_LL_SetToggle (phost,  AUDIO_Handle->control.Pipe, 0);          
   
      }
      
      AUDIO_Handle->req_state     = AUDIO_REQ_INIT;
      AUDIO_Handle->control_state = AUDIO_CONTROL_INIT;
      
      status = USBH_OK;
    }
  }
  return status;
}



/**
  * @brief  USBH_AUDIO_InterfaceDeInit 
  *         The function DeInit the Pipes used for the Audio class.
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_InterfaceDeInit (USBH_HandleTypeDef *phost)
{
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; 
  
  if(AUDIO_Handle->microphone.Pipe != 0x00)
  {   
    USBH_ClosePipe  (phost, AUDIO_Handle->microphone.Pipe);
    USBH_FreePipe  (phost, AUDIO_Handle->microphone.Pipe);
    AUDIO_Handle->microphone.Pipe = 0;     /* Reset the pipe as Free */  
  }
  
  if( AUDIO_Handle->headphone.Pipe != 0x00)
  {   
    USBH_ClosePipe(phost,  AUDIO_Handle->headphone.Pipe);
    USBH_FreePipe  (phost,  AUDIO_Handle->headphone.Pipe);
    AUDIO_Handle->headphone.Pipe = 0;     /* Reset the pipe as Free */  
  }
  
  if( AUDIO_Handle->control.Pipe != 0x00)
  {   
    USBH_ClosePipe(phost,  AUDIO_Handle->control.Pipe);
    USBH_FreePipe  (phost,  AUDIO_Handle->control.Pipe);
    AUDIO_Handle->control.Pipe = 0;     /* Reset the pipe as Free */  
  }
  
  if(phost->pActiveClass->pData)
  {
    USBH_free (phost->pActiveClass->pData);
    phost->pActiveClass->pData = 0;
  }  
  return USBH_OK ;
}

/**
  * @brief  USBH_AUDIO_ClassRequest 
  *         The function is responsible for handling Standard requests
  *         for Audio class.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_ClassRequest(USBH_HandleTypeDef *phost)
{   
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;  
  USBH_StatusTypeDef status = USBH_BUSY;
  USBH_StatusTypeDef req_status = USBH_BUSY;
  
  /* Switch AUDIO REQ state machine */
  switch (AUDIO_Handle->req_state)
  {
  case AUDIO_REQ_INIT:
  case AUDIO_REQ_SET_DEFAULT_IN_INTERFACE:
    if(AUDIO_Handle->microphone.supported == 1)
    {
      req_status = USBH_SetInterface(phost, 
                                     AUDIO_Handle->microphone.interface, 
                                     0);
      
      if(req_status == USBH_OK)
      {
        AUDIO_Handle->req_state = AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE;
      }
      
    }
    else
    {
      AUDIO_Handle->req_state = AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif       
    }
    break;
    
  case AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE:
    if(AUDIO_Handle->headphone.supported == 1)
    {
      req_status = USBH_SetInterface(phost, 
                                     AUDIO_Handle->headphone.interface, 
                                     0);
      
      if(req_status == USBH_OK)
      {
        AUDIO_Handle->req_state = AUDIO_REQ_CS_REQUESTS;
        AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME;
        
        AUDIO_Handle->temp_feature  = AUDIO_Handle->headphone.asociated_feature;
        AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels;
      }
    }
    else
    {
        AUDIO_Handle->req_state = AUDIO_REQ_CS_REQUESTS;
        AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif         
    }
    break;
    
  case AUDIO_REQ_CS_REQUESTS:
    if(USBH_AUDIO_HandleCSRequest (phost) == USBH_OK)
    {
      AUDIO_Handle->req_state = AUDIO_REQ_SET_IN_INTERFACE;
    }
    break;
   
  case AUDIO_REQ_SET_IN_INTERFACE:
    if(AUDIO_Handle->microphone.supported == 1)
    {
      req_status = USBH_SetInterface(phost, 
                                     AUDIO_Handle->microphone.interface, 
                                     AUDIO_Handle->microphone.AltSettings);
      
      if(req_status == USBH_OK)
      {
        AUDIO_Handle->req_state = AUDIO_REQ_SET_OUT_INTERFACE;
      }
    }
    else
    {
      AUDIO_Handle->req_state = AUDIO_REQ_SET_OUT_INTERFACE;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif       
    }
    break;
  case AUDIO_REQ_SET_OUT_INTERFACE:
   if(AUDIO_Handle->headphone.supported == 1)
    {
      req_status = USBH_SetInterface(phost, 
                                     AUDIO_Handle->headphone.interface, 
                                     AUDIO_Handle->headphone.AltSettings);
      
      if(req_status == USBH_OK)
      {
        AUDIO_Handle->req_state = AUDIO_REQ_IDLE;
      }
      
    }
   else
   {
     AUDIO_Handle->req_state = AUDIO_REQ_IDLE;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif      
   }
   break;
  case AUDIO_REQ_IDLE:
    AUDIO_Handle->play_state = AUDIO_PLAYBACK_INIT;
    phost->pUser(phost, HOST_USER_CLASS_ACTIVE); 
    status  = USBH_OK;    
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_CLASS_EVENT, 0);
#endif
  default:
    break;
  }
  return status; 
}

/**
  * @brief  USBH_AUDIO_CSRequest 
  *         The function is responsible for handling AC Specific requests for a specific feature and channel
  *         for Audio class.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_CSRequest(USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel)
{   
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;  
  USBH_StatusTypeDef status = USBH_BUSY;
  USBH_StatusTypeDef req_status = USBH_BUSY;
  
  /* Switch AUDIO REQ state machine */
  switch (AUDIO_Handle->cs_req_state)
  {
  case AUDIO_REQ_GET_VOLUME:
    req_status = USBH_AC_GetCur(phost,                 
                                UAC_FEATURE_UNIT,     /* subtype  */
                                feature,              /* feature  */
                                VOLUME_CONTROL,       /* Selector */
                                channel,              /* channel  */
                                0x02);                /* length   */
    if(req_status != USBH_BUSY)
    {
      AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_MIN_VOLUME;
      AUDIO_Handle->headphone.attribute.volume = LE16(&(AUDIO_Handle->mem[0]));
    }
    break;
    
  case AUDIO_REQ_GET_MIN_VOLUME:
    req_status = USBH_AC_GetMin(phost,                 
                                UAC_FEATURE_UNIT,     /* subtype  */
                                feature,              /* feature  */
                                VOLUME_CONTROL,       /* Selector */
                                channel,              /* channel  */
                                0x02);                /* length   */
    if(req_status != USBH_BUSY)
    {
      AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_MAX_VOLUME;
      AUDIO_Handle->headphone.attribute.volumeMin = LE16(&AUDIO_Handle->mem[0]);
    }
    break;

  case AUDIO_REQ_GET_MAX_VOLUME:
    req_status = USBH_AC_GetMax(phost,                 
                                UAC_FEATURE_UNIT,     /* subtype  */
                                feature,              /* feature  */
                                VOLUME_CONTROL,       /* Selector */
                                channel,              /* channel  */
                                0x02);                /* length   */
    if(req_status != USBH_BUSY)
    {
      AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_RESOLUTION;
      AUDIO_Handle->headphone.attribute.volumeMax = LE16(&AUDIO_Handle->mem[0]);   
      
      if (AUDIO_Handle->headphone.attribute.volumeMax < AUDIO_Handle->headphone.attribute.volumeMin)
      {
        AUDIO_Handle->headphone.attribute.volumeMax = 0xFF00; 
      }
    }
    break;
    
  case AUDIO_REQ_GET_RESOLUTION:
    req_status = USBH_AC_GetRes(phost,                 
                                UAC_FEATURE_UNIT,     /* subtype  */
                                feature,              /* feature  */
                                VOLUME_CONTROL,       /* Selector */
                                channel,              /* channel  */
                                0x02);                /* length   */
    if(req_status != USBH_BUSY)
    {
      AUDIO_Handle->cs_req_state = AUDIO_REQ_CS_IDLE;
      AUDIO_Handle->headphone.attribute.resolution = LE16(&AUDIO_Handle->mem[0]);    
    }
    break;
    
 
  case AUDIO_REQ_CS_IDLE:
    status = USBH_OK;
  default:
    break;
  }
  return status; 
}

/**
  * @brief  USBH_AUDIO_HandleCSRequest 
  *         The function is responsible for handling AC Specific requests for a all features 
  *         and associated channels for Audio class.
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_HandleCSRequest(USBH_HandleTypeDef *phost)
{ 

  USBH_StatusTypeDef status = USBH_BUSY;
  USBH_StatusTypeDef cs_status = USBH_BUSY;
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;  
        
  cs_status = USBH_AUDIO_CSRequest(phost, 
                                   AUDIO_Handle->temp_feature, 
                                   AUDIO_Handle->temp_channels);
  
  if(cs_status != USBH_BUSY)
  {
    
    if(AUDIO_Handle->temp_channels ==  1)
    {
        AUDIO_Handle->temp_feature = AUDIO_Handle->headphone.asociated_feature;
        AUDIO_Handle->temp_channels = 0;
        status = USBH_OK; 
    }
    else
    {
      AUDIO_Handle->temp_channels--;
    }
    AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif     
  }
  
  return status;
}

/**
  * @brief  USBH_AUDIO_Process 
  *         The function is for managing state machine for Audio data transfers 
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_Process (USBH_HandleTypeDef *phost)
{   
  USBH_StatusTypeDef status = USBH_BUSY;
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef *)  phost->pActiveClass->pData;  
  
  if(AUDIO_Handle->headphone.supported == 1)
  {
    USBH_AUDIO_OutputStream (phost);
  }
  
  if(AUDIO_Handle->microphone.supported == 1)
  {
    USBH_AUDIO_InputStream (phost);
  }
 
  return status;
}

/**
  * @brief  USBH_AUDIO_SOFProcess 
  *         The function is for managing the SOF callback
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_SOFProcess (USBH_HandleTypeDef *phost)
{  
  return USBH_OK;
}
/**
  * @brief  Find IN Audio Streaming interfaces
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingIN(USBH_HandleTypeDef *phost)
{
  uint8_t interface, alt_settings;
  USBH_StatusTypeDef status = USBH_FAIL ;
  AUDIO_HandleTypeDef *AUDIO_Handle;

  AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; 

  /* Look For AUDIOSTREAMING IN interface */
  alt_settings = 0;
  for (interface = 0;  interface < USBH_MAX_NUM_INTERFACES ; interface ++ )
  {
    if((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == AC_CLASS)&&
       (phost->device.CfgDesc.Itf_Desc[interface].bInterfaceSubClass == USB_SUBCLASS_AUDIOSTREAMING))
    {
      if((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80)&&
         (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0))
      {
        AUDIO_Handle->stream_in[alt_settings].Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
        AUDIO_Handle->stream_in[alt_settings].EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
        AUDIO_Handle->stream_in[alt_settings].interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber;        
        AUDIO_Handle->stream_in[alt_settings].AltSettings = phost->device.CfgDesc.Itf_Desc[interface].bAlternateSetting;
        AUDIO_Handle->stream_in[alt_settings].Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval;   
        AUDIO_Handle->stream_in[alt_settings].valid = 1; 
        alt_settings++;
      }
    }
  } 
  
  if(alt_settings > 0)
  {  
     status = USBH_OK;
  }
  
  return status;
}

/**
  * @brief  Find OUT Audio Streaming interfaces
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingOUT(USBH_HandleTypeDef *phost)
{
  uint8_t interface, alt_settings;
  USBH_StatusTypeDef status = USBH_FAIL ;
  AUDIO_HandleTypeDef *AUDIO_Handle;

  AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; 

  /* Look For AUDIOSTREAMING IN interface */
  alt_settings = 0;
  for (interface = 0;  interface < USBH_MAX_NUM_INTERFACES ; interface ++ )
  {
    if((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == AC_CLASS)&&
       (phost->device.CfgDesc.Itf_Desc[interface].bInterfaceSubClass == USB_SUBCLASS_AUDIOSTREAMING))
    {
      if(((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80) == 0x00)&&
         (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0))        
      {
        AUDIO_Handle->stream_out[alt_settings].Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
        AUDIO_Handle->stream_out[alt_settings].EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
        AUDIO_Handle->stream_out[alt_settings].interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber;
        AUDIO_Handle->stream_out[alt_settings].AltSettings = phost->device.CfgDesc.Itf_Desc[interface].bAlternateSetting;
        AUDIO_Handle->stream_out[alt_settings].Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval;           
        AUDIO_Handle->stream_out[alt_settings].valid = 1; 
        alt_settings++;
      }
    }
  }
  
  if(alt_settings > 0)
  {
     status = USBH_OK;
  }
  
  return status;  
}

/**
  * @brief  Find HID Control interfaces
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_FindHIDControl(USBH_HandleTypeDef *phost)
{
  uint8_t interface;
  USBH_StatusTypeDef status = USBH_FAIL ;
  AUDIO_HandleTypeDef *AUDIO_Handle;

  AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;   

  /* Look For AUDIOCONTROL  interface */
  interface = USBH_FindInterface(phost, AC_CLASS, USB_SUBCLASS_AUDIOCONTROL, 0xFF);
  if(interface != 0xFF)
  {
    for (interface = 0;  interface < USBH_MAX_NUM_INTERFACES ; interface ++ )
    {
      if((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == 0x03)&& /*HID*/
        (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0))
      {
        if((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80) == 0x80)
        {
          AUDIO_Handle->control.Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
          AUDIO_Handle->control.EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
          AUDIO_Handle->control.interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber;          
          AUDIO_Handle->control.Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval;
          AUDIO_Handle->control.supported = 1;
          status = USBH_OK;
          break;
        }
      }
    } 
  }
  return status;
}

/**
  * @brief  Parse AC and interfaces Descriptors
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_ParseCSDescriptors(USBH_HandleTypeDef *phost)
{
  USBH_DescHeader_t            *pdesc ;
  uint16_t                      ptr;
  int8_t                        itf_index = 0;
  int8_t                        itf_number = 0; 
  int8_t                        alt_setting;   
  AUDIO_HandleTypeDef           *AUDIO_Handle;
  
  AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;  
  pdesc   = (USBH_DescHeader_t *)(phost->device.CfgDesc_Raw);
  ptr = USB_LEN_CFG_DESC;
  
  AUDIO_Handle->class_desc.FeatureUnitNum = 0;
  AUDIO_Handle->class_desc.InputTerminalNum = 0;
  AUDIO_Handle->class_desc.OutputTerminalNum = 0;  
  AUDIO_Handle->class_desc.ASNum = 0;
  
  while(ptr < phost->device.CfgDesc.wTotalLength)
  {
    pdesc = USBH_GetNextDesc((uint8_t*) pdesc, &ptr);
    
    switch (pdesc->bDescriptorType)
    {
      
    case USB_DESC_TYPE_INTERFACE:
      itf_number = *((uint8_t *)pdesc + 2);
      alt_setting = *((uint8_t *)pdesc + 3);
      itf_index = USBH_FindInterfaceIndex (phost, itf_number, alt_setting);     
      break;
      
    case USB_DESC_TYPE_CS_INTERFACE:
      if(itf_number <= phost->device.CfgDesc.bNumInterfaces)
      {
        
        ParseCSDescriptors(&AUDIO_Handle->class_desc,
                           phost->device.CfgDesc.Itf_Desc[itf_index].bInterfaceSubClass, 
                           (uint8_t *)pdesc);
      }
      break;
      
    default:
      break; 
    }
  }
  return USBH_OK;
}

/**
  * @brief  Parse AC interfaces
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef ParseCSDescriptors(AUDIO_ClassSpecificDescTypedef *class_desc, 
                                      uint8_t ac_subclass, 
                                      uint8_t *pdesc)
{
  if(ac_subclass == USB_SUBCLASS_AUDIOCONTROL)
  {
    switch(pdesc[2])
    {
    case UAC_HEADER: 
      class_desc->cs_desc.HeaderDesc = (AUDIO_HeaderDescTypeDef *)pdesc;    
      break;
      
    case UAC_INPUT_TERMINAL:
      class_desc->cs_desc.InputTerminalDesc[class_desc->InputTerminalNum++] = (AUDIO_ITDescTypeDef*) pdesc;    
      break;
      
    case UAC_OUTPUT_TERMINAL:
      class_desc->cs_desc.OutputTerminalDesc[class_desc->OutputTerminalNum++] = (AUDIO_OTDescTypeDef*) pdesc;   
      break;

    case UAC_FEATURE_UNIT:
      class_desc->cs_desc.FeatureUnitDesc[class_desc->FeatureUnitNum++] = (AUDIO_FeatureDescTypeDef*) pdesc; 
      break;
      
    case UAC_SELECTOR_UNIT:
      class_desc->cs_desc.SelectorUnitDesc[class_desc->SelectorUnitNum++] = (AUDIO_SelectorDescTypeDef*) pdesc; 
      break;

    case UAC_MIXER_UNIT:
      class_desc->cs_desc.MixerUnitDesc[class_desc->MixerUnitNum++] = (AUDIO_MixerDescTypeDef*) pdesc; 
      break;      

    default: 
      break;
    }
  }
  else if(ac_subclass == USB_SUBCLASS_AUDIOSTREAMING)
  {
    switch(pdesc[2])
    {
    case UAC_AS_GENERAL:
      class_desc->as_desc[class_desc->ASNum].GeneralDesc = (AUDIO_ASGeneralDescTypeDef*) pdesc; 
      break;
    case UAC_FORMAT_TYPE:      
      class_desc->as_desc[class_desc->ASNum++].FormatTypeDesc = (AUDIO_ASFormatTypeDescTypeDef*) pdesc; 
      break;
    default:
      break;
    }
  }
 
  return USBH_OK;
}


/**
  * @brief  Link a Unit to next associated one
  * @param  phost: Host handle
  * @param  UnitID: Unit identifer
  * @retval UnitID, Index and Type of the associated Unit 
  */
static int32_t USBH_AUDIO_FindLinkedUnit(USBH_HandleTypeDef *phost, uint8_t UnitID)
{
  uint8_t Index;  
  AUDIO_HandleTypeDef *AUDIO_Handle;
  
  AUDIO_Handle =  (AUDIO_HandleTypeDef *) phost->pActiveClass->pData;    
  
  /* Find Feature Unit */
  for(Index = 0; Index < AUDIO_Handle->class_desc.FeatureUnitNum; Index ++)
  {
    if(AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[Index]->bSourceID == UnitID)
    {
      UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[Index]->bUnitID;
      
      return ((UnitID << 16) | (UAC_FEATURE_UNIT << 8) | Index);
    }
  }
  
  /* Find Mixer Unit */
  for(Index = 0; Index < AUDIO_Handle->class_desc.MixerUnitNum; Index ++)
  {
    if((AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bSourceID0 == UnitID)||
       (AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bSourceID1 == UnitID))
    {
      UnitID = AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bUnitID;
      
      return ((UnitID << 16) | (UAC_MIXER_UNIT << 8) | Index);
    }
  }
  
  
  /* Find Selector Unit */
  for(Index = 0; Index < AUDIO_Handle->class_desc.SelectorUnitNum; Index ++)
  {
    if(AUDIO_Handle->class_desc.cs_desc.SelectorUnitDesc[Index]->bSourceID0 == UnitID)
    {
      UnitID = AUDIO_Handle->class_desc.cs_desc.SelectorUnitDesc[Index]->bUnitID;
      
      return ((UnitID << 16) | (UAC_SELECTOR_UNIT << 8) | Index); 
    }
  }  
  
  
  /* Find OT Unit */  
  for(Index = 0; Index < AUDIO_Handle->class_desc.OutputTerminalNum; Index ++)
  {
    if(AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->bSourceID == UnitID)
    {
      UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->bTerminalID;
      
      return ((UnitID << 16) | (UAC_OUTPUT_TERMINAL << 8) | Index);
    }
  } 
  
  /* No associated Unit found */
  return -1;
}

/**
  * @brief  Build full path for Microphone device
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_BuildMicrophonePath(USBH_HandleTypeDef *phost)
{
  uint8_t UnitID = 0, Type, Index;
  uint32_t value;
  uint8_t terminalIndex;  
  AUDIO_HandleTypeDef *AUDIO_Handle;
  
  AUDIO_Handle = (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;    
  
  /*Find microphone IT*/
  for(terminalIndex = 0; terminalIndex < AUDIO_Handle->class_desc.InputTerminalNum; terminalIndex++)
  {
    if(LE16(AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->wTerminalType) == 0x201)
    {    
      UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bTerminalID;
      AUDIO_Handle->microphone.asociated_channels =  AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bNrChannels;
      break;
    }
  }
  
  do
  {
    value =  USBH_AUDIO_FindLinkedUnit(phost, UnitID);
    Index = value & 0xFF;
    Type   = (value >> 8) & 0xFF;
    UnitID   = (value >> 16) & 0xFF;
    
    switch (Type)
    {
    case UAC_FEATURE_UNIT:
      AUDIO_Handle->microphone.asociated_feature = Index;
      break;
      
    case UAC_MIXER_UNIT:
      AUDIO_Handle->microphone.asociated_mixer = Index;
      break;
      
    case UAC_SELECTOR_UNIT:
      AUDIO_Handle->microphone.asociated_selector = Index;
      break;
      
    case UAC_OUTPUT_TERMINAL:
      AUDIO_Handle->microphone.asociated_terminal = Index;
      break;    
    }
  }
  while ((Type != UAC_OUTPUT_TERMINAL) && (value > 0));
  
  
  
  return USBH_OK;
}

/**
  * @brief  Build full path for Headphone device
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_BuildHeadphonePath(USBH_HandleTypeDef *phost)
{
  uint8_t UnitID = 0, Type, Index;
  uint32_t value;
  uint8_t terminalIndex;  
  AUDIO_HandleTypeDef *AUDIO_Handle;
  
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;    
  
  /* Find association between audio streaming and microphone */
  for(terminalIndex = 0; terminalIndex < AUDIO_Handle->class_desc.InputTerminalNum; terminalIndex++)
  {
    if(LE16(AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->wTerminalType) == 0x101)
    {    
      UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bTerminalID;
      AUDIO_Handle->headphone.asociated_channels =  AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bNrChannels;
      break;
    }
  }
  
  for(Index = 0; Index < AUDIO_Handle->class_desc.ASNum; Index++)
  {
    if(AUDIO_Handle->class_desc.as_desc[Index].GeneralDesc->bTerminalLink == UnitID)
    {    
      AUDIO_Handle->headphone.asociated_as = Index;
      break;
    }
  } 
        
  do
  {
    value =  USBH_AUDIO_FindLinkedUnit(phost, UnitID);
    Index = value & 0xFF;
    Type   = (value >> 8) & 0xFF;
    UnitID   = (value >> 16) & 0xFF;
    
    switch (Type)
    {
    case UAC_FEATURE_UNIT:
      AUDIO_Handle->headphone.asociated_feature = Index;
      break;
      
    case UAC_MIXER_UNIT:
      AUDIO_Handle->headphone.asociated_mixer = Index;
      break;
      
    case UAC_SELECTOR_UNIT:
      AUDIO_Handle->headphone.asociated_selector = Index;
      break;
      
    case UAC_OUTPUT_TERMINAL:
      AUDIO_Handle->headphone.asociated_terminal = Index;
      if(LE16(AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->wTerminalType) != 0x103)
      {
        return  USBH_OK;
      }
      break;    
    }
  }
  while ((Type != UAC_OUTPUT_TERMINAL) && (value > 0));

  return USBH_FAIL;
}


/**
  * @brief  Handle Set Cur request
  * @param  phost: Host handle
  * @param  subtype: subtype index
  * @param  feature: feature index
  * @param  controlSelector: control code
  * @param  channel: channel index
  * @param  length: Command length
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AC_SetCur(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length)
{
  uint16_t wValue,wIndex,wLength;
  uint8_t UnitID,InterfaceNum;
  AUDIO_HandleTypeDef *AUDIO_Handle;
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch(subtype)
  {
  case UAC_INPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ;
    AUDIO_Handle->mem[0] = 0x00;
    
    wLength = 1;
    break;
  case UAC_FEATURE_UNIT:
    UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    /*holds the CS(control selector ) and CN (channel number)*/
    wValue =  (controlSelector << 8) | channel;
    wLength = length;
    break;
  }
  
  phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_INTERFACE | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_SET_CUR;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)(AUDIO_Handle->mem) , wLength ));    
  
}

/**
  * @brief  Handle Get Cur request
  * @param  phost: Host handle
  * @param  subtype: subtype index
  * @param  feature: feature index
  * @param  controlSelector: control code
  * @param  channel: channel index
  * @param  length: Command length
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AC_GetCur(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length)
{
  uint16_t wValue = 0, wIndex = 0,wLength = 0;
  uint8_t UnitID = 0, InterfaceNum = 0;
  AUDIO_HandleTypeDef *AUDIO_Handle;
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch(subtype)
  {
  case UAC_INPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ;
    AUDIO_Handle->mem[0] = 0x00;
    
    wLength = 1;
    break;
  case UAC_FEATURE_UNIT:
    UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    /*holds the CS(control selector ) and CN (channel number)*/
    wValue =  (controlSelector << 8) | channel;
    wLength = length;
    break;
    
  case UAC_OUTPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ; 
    wLength = 1; 
    break;
  }
  
  phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_GET_CUR;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)(AUDIO_Handle->mem) , wLength ));    
  
}


/**
  * @brief  Handle Get Max request
  * @param  phost: Host handle
  * @param  subtype: subtype index
  * @param  feature: feature index
  * @param  controlSelector: control code
  * @param  channel: channel index
  * @param  length: Command length
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AC_GetMax(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length)
{
  uint16_t wValue = 0, wIndex = 0, wLength = 0;
  uint8_t UnitID = 0, InterfaceNum = 0;
  AUDIO_HandleTypeDef *AUDIO_Handle;
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch(subtype)
  {
  case UAC_INPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ;
    AUDIO_Handle->mem[0] = 0x00;
    
    wLength = 1;
    break;
  case UAC_FEATURE_UNIT:
    UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    /*holds the CS(control selector ) and CN (channel number)*/
    wValue =  (controlSelector << 8) | channel;
    wLength = length;
    break;
    
  case UAC_OUTPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ; 
    wLength = 1; 
    break;
  }
  
  phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_GET_MAX;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)(AUDIO_Handle->mem) , wLength ));    
  
}



/**
  * @brief  Handle Get Res request
  * @param  phost: Host handle
  * @param  subtype: subtype index
  * @param  feature: feature index
  * @param  controlSelector: control code
  * @param  channel: channel index
  * @param  length: Command length
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AC_GetRes(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length)
{
  uint16_t wValue = 0, wIndex = 0, wLength = 0;
  uint8_t UnitID = 0, InterfaceNum = 0;
  AUDIO_HandleTypeDef *AUDIO_Handle;
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch(subtype)
  {
  case UAC_INPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ;
    AUDIO_Handle->mem[0] = 0x00;
    
    wLength = 1;
    break;
  case UAC_FEATURE_UNIT:
    UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    /*holds the CS(control selector ) and CN (channel number)*/
    wValue =  (controlSelector << 8) | channel;
    wLength = length;
    break;
    
  case UAC_OUTPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ; 
    wLength = 1; 
    break;
  }
  
  phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_GET_RES;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)(AUDIO_Handle->mem) , wLength ));    
  
}

/**
  * @brief  Handle Get Min request
  * @param  phost: Host handle
  * @param  subtype: subtype index
  * @param  feature: feature index
  * @param  controlSelector: control code
  * @param  channel: channel index
  * @param  length: Command length
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AC_GetMin(USBH_HandleTypeDef *phost, 
                                         uint8_t subtype, 
                                         uint8_t feature,
                                         uint8_t controlSelector,
                                         uint8_t channel,
                                         uint16_t length)
{
  uint16_t wValue = 0, wIndex = 0, wLength = 0;
  uint8_t UnitID = 0, InterfaceNum = 0;
  AUDIO_HandleTypeDef *AUDIO_Handle;
  AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch(subtype)
  {
  case UAC_INPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ;
    AUDIO_Handle->mem[0] = 0x00;
    
    wLength = 1;
    break;
  case UAC_FEATURE_UNIT:
    UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    /*holds the CS(control selector ) and CN (channel number)*/
    wValue =  (controlSelector << 8) | channel;
    wLength = length;
    break;
    
  case UAC_OUTPUT_TERMINAL:
    UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID;
    InterfaceNum = 0; /*Always zero Control Interface */
    wIndex = ( UnitID << 8 ) | InterfaceNum ;
    wValue = (COPY_PROTECT_CONTROL << 8 ) ; 
    wLength = 1; 
    break;
  }
  
  phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_GET_MIN;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)(AUDIO_Handle->mem) , wLength ));    
  
}

/**
  * @brief  Handle Set Endpoint Controls Request
  * @param  phost: Host handle
  * @param  Ep: Endpoint address
  * @param  buf: pointer to data
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_SetEndpointControls(USBH_HandleTypeDef *phost,
                                               uint8_t  Ep,
                                               uint8_t *buff) 
{
  uint16_t wValue, wIndex, wLength;
  

  wValue = SAMPLING_FREQ_CONTROL << 8;
  wIndex = Ep; 
  wLength = 3; /*length of the frequency parameter*/   
  
  phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_ENDPOINT | \
    USB_REQ_TYPE_CLASS;
  
  phost->Control.setup.b.bRequest = UAC_SET_CUR;
  phost->Control.setup.b.wValue.w = wValue;
  phost->Control.setup.b.wIndex.w = wIndex;
  phost->Control.setup.b.wLength.w = wLength; 
  
  return(USBH_CtlReq(phost, (uint8_t *)buff, wLength ));
    
}

/**
  * @brief  Handle Input stream process
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_InputStream (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_BUSY ; 
  
  return status;  
}

/**
  * @brief  Handle HID Control process
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_Control (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_BUSY ;
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData; 
  uint16_t attribute  = 0; 
 
  
  switch(AUDIO_Handle->control_state)
  {
  case AUDIO_CONTROL_INIT:
    if((phost->Timer & 1) == 0)
    {
      AUDIO_Handle->control.timer = phost->Timer;
      USBH_InterruptReceiveData(phost, 
                                (uint8_t *)(AUDIO_Handle->mem),
                                AUDIO_Handle->control.EpSize,
                                AUDIO_Handle->control.Pipe);
      
      AUDIO_Handle->temp_feature  = AUDIO_Handle->headphone.asociated_feature;
      AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels;
      
      AUDIO_Handle->control_state = AUDIO_CONTROL_CHANGE ;
    }
    break;
  case AUDIO_CONTROL_CHANGE:
    if(USBH_LL_GetURBState(phost , AUDIO_Handle->control.Pipe) == USBH_URB_DONE)
    {
      attribute = LE16(&AUDIO_Handle->mem[0]); 
      if(USBH_AUDIO_SetControlAttribute (phost, attribute) == USBH_BUSY)
      {
        break;
      }
    }
    
    if(( phost->Timer - AUDIO_Handle->control.timer) >= AUDIO_Handle->control.Poll)
    {
      AUDIO_Handle->control.timer = phost->Timer;
      
      USBH_InterruptReceiveData(phost, 
                                (uint8_t *)(AUDIO_Handle->mem),
                                AUDIO_Handle->control.EpSize,
                                AUDIO_Handle->control.Pipe);      
      
    }
    break;
    
  case AUDIO_CONTROL_VOLUME_UP:
       if( USBH_AUDIO_SetControlAttribute (phost, 1) == USBH_OK)
       {
         AUDIO_Handle->control_state = AUDIO_CONTROL_INIT;
        status = USBH_OK; 
       }
       break;
       
  case AUDIO_CONTROL_VOLUME_DOWN:
       if( USBH_AUDIO_SetControlAttribute (phost, 2) == USBH_OK)
       {
         AUDIO_Handle->control_state = AUDIO_CONTROL_INIT;         
         status = USBH_OK; 
       }     
       break;
       
  case AUDIO_CONTROL_IDLE:
  default:  
    break; 
  }
    
  return status;  
}

/**
  * @brief  Handle Output stream process
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_OutputStream (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_BUSY ;
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;   
  uint8_t *buff;
  
  
  switch(AUDIO_Handle->play_state)
  {
  case AUDIO_PLAYBACK_INIT:
    
    if( AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bSamFreqType == 0)
    {
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP_FREQ;        
    }
    else
    {
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP;
    }
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);
#endif  
    break;
    
  case AUDIO_PLAYBACK_SET_EP_FREQ:

    buff = (uint8_t*)AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[0];    

    status = USBH_AUDIO_SetEndpointControls(phost, AUDIO_Handle->headphone.Ep, buff);
    if(status == USBH_OK) 
    {
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE;    
    }
    break;
    
  case AUDIO_PLAYBACK_SET_EP:
    buff = (uint8_t *)&AUDIO_Handle->headphone.frequency;    
    status = USBH_AUDIO_SetEndpointControls(phost,AUDIO_Handle->headphone.Ep, buff);
    if(status == USBH_OK)
    {
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE;
      USBH_AUDIO_FrequencySet(phost);
    }
    break;
  case AUDIO_PLAYBACK_IDLE:
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_CLASS_EVENT, 0);
#endif      
    status = USBH_OK;
    break;
    
  case AUDIO_PLAYBACK_PLAY:
    USBH_AUDIO_Transmit(phost);
    status = USBH_OK;
    break;
    
  default:
    break;
  }  
  
  return status;
}

/**
  * @brief  Handle Transmission process
  * @param  phost: Host handle
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_Transmit (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef status = USBH_BUSY ;
  AUDIO_HandleTypeDef *AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;   
  
  switch(AUDIO_Handle->processing_state)
  {
  case AUDIO_DATA_START_OUT:
    /* Sync with start of Even Frame */
    if((phost->Timer & 1) == 0)
    {
      AUDIO_Handle->headphone.timer = phost->Timer;
      AUDIO_Handle->processing_state = AUDIO_DATA_OUT;
      USBH_IsocSendData(phost, 
                       AUDIO_Handle->headphone.buf,
                       AUDIO_Handle->headphone.frame_length,
                       AUDIO_Handle->headphone.Pipe);
      
      AUDIO_Handle->headphone.partial_ptr = AUDIO_Handle->headphone.frame_length; 
      AUDIO_Handle->headphone.global_ptr = AUDIO_Handle->headphone.frame_length; 
      AUDIO_Handle->headphone.cbuf = AUDIO_Handle->headphone.buf;
    }
    else
    {
#if (USBH_USE_OS == 1)
      osDelay(1);
      osMessagePut ( phost->os_event, USBH_CLASS_EVENT, 0);
#endif  
    }
    break;
    
  case AUDIO_DATA_OUT:
    if((USBH_LL_GetURBState(phost , AUDIO_Handle->headphone.Pipe) == USBH_URB_DONE)&&
       (( phost->Timer - AUDIO_Handle->headphone.timer) >= AUDIO_Handle->headphone.Poll))
    {
      AUDIO_Handle->headphone.timer = phost->Timer;
      
      if(AUDIO_Handle->control.supported == 1)
      {
        USBH_AUDIO_Control (phost);
      }
      
      if(AUDIO_Handle->headphone.global_ptr <= AUDIO_Handle->headphone.total_length)
      {
        USBH_IsocSendData(phost, 
                          AUDIO_Handle->headphone.cbuf,
                          AUDIO_Handle->headphone.frame_length,
                          AUDIO_Handle->headphone.Pipe);
        
        AUDIO_Handle->headphone.cbuf += AUDIO_Handle->headphone.frame_length;
        AUDIO_Handle->headphone.partial_ptr += AUDIO_Handle->headphone.frame_length;
        AUDIO_Handle->headphone.global_ptr += AUDIO_Handle->headphone.frame_length;
      }
      else
      {
       AUDIO_Handle->headphone.partial_ptr = 0xFFFFFFFF;
       AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE;
       USBH_AUDIO_BufferEmptyCallback(phost);
      }
    }
    break;
  }
  return status;
}

/**
  * @brief  USBH_AUDIO_SetFrequency 
  *         Set Audio sampling parameters
  * @param  phost: Host handle
  * @param  SampleRate: Sample Rate
  * @param  NbrChannels: Number of Channels
  * @param  BitPerSample: Bit Per Sample
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_SetFrequency (USBH_HandleTypeDef *phost, 
                                            uint16_t SampleRate,
                                            uint8_t  NbrChannels,
                                            uint8_t  BitPerSample)
{
  USBH_StatusTypeDef Status = USBH_BUSY;
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  uint8_t              index; 
  uint8_t              change_freq = FALSE;
  uint32_t             freq_min, freq_max;
  uint8_t              num_supported_freq;
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE)
    {
      
      if(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bSamFreqType == 0)
      { 
        freq_min = LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[0]); 
        freq_max = LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[1]);         
        
        if(( SampleRate >= freq_min)&& (SampleRate <= freq_max))
        {
          change_freq = TRUE;
        }
      }
      else
      {    
        
        num_supported_freq = (AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bLength - 8)/3;
        
        
        for(index = 0; index < num_supported_freq; index++)
        {
          if(SampleRate == LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[index]))
          {
            change_freq = TRUE;            
            break;
          }
        }  
      }      
      
      if(change_freq == TRUE)
      {
        AUDIO_Handle->headphone.frequency = SampleRate;
        AUDIO_Handle->headphone.frame_length    = (SampleRate * BitPerSample * NbrChannels) / 8000;
        AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP;
        Status = USBH_OK;  
        
      }
    }
  }
   return Status;  
}

/**
  * @brief  USBH_AUDIO_Play 
  *         Start playback process
  * @param  phost: Host handle
  * @param  buf: pointer to raw audio data
  * @param  length: total length of the audio data
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_Play (USBH_HandleTypeDef *phost, uint8_t *buf, uint32_t length)
{
  USBH_StatusTypeDef Status = USBH_FAIL;
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE)
    {
      AUDIO_Handle->headphone.buf = buf;
      AUDIO_Handle->headphone.total_length = length;
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_PLAY;
      AUDIO_Handle->control_state = AUDIO_CONTROL_INIT;
      AUDIO_Handle->processing_state = AUDIO_DATA_START_OUT;
      Status = USBH_OK;
#if (USBH_USE_OS == 1)
      osMessagePut ( phost->os_event, USBH_CLASS_EVENT, 0);
#endif        
    }
  }
  return Status;  
}

/**
  * @brief  USBH_AUDIO_Pause 
  *         Stop the playback process
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_Stop (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef Status = USBH_FAIL;
  Status = USBH_AUDIO_Suspend(phost);
  return Status;  
}

/**
  * @brief  USBH_AUDIO_Suspend 
  *         Suspend the playback process
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_Suspend (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef Status = USBH_FAIL;
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY)
    {
      AUDIO_Handle->control_state = AUDIO_CONTROL_IDLE;
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE;
      Status = USBH_OK;      
    }
  }
  return Status;  
}
/**
  * @brief  USBH_AUDIO_Resume 
  *         Resume the playback process
  * @param  phost: Host handle
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_Resume (USBH_HandleTypeDef *phost)
{
  USBH_StatusTypeDef Status = USBH_FAIL;
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE)
    {
      AUDIO_Handle->control_state = AUDIO_CONTROL_INIT;
      AUDIO_Handle->play_state = AUDIO_PLAYBACK_PLAY;
    }
  }
  return Status;  
}
/**
  * @brief  USBH_AUDIO_GetOutOffset 
  *         return the current buffer pointer for OUT process
  * @param  phost: Host handle
  * @retval USBH Status
  */
int32_t USBH_AUDIO_GetOutOffset (USBH_HandleTypeDef *phost)
{
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY)
    {
      return AUDIO_Handle->headphone.partial_ptr;
    }
  }
  return -1;  
}

/**
  * @brief  USBH_AUDIO_ChangeOutBuffer 
  *         Change audio data buffer address
  * @param  phost: Host handle
  * @param  buf: buffer address
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_ChangeOutBuffer (USBH_HandleTypeDef *phost, uint8_t *buf)
{
  USBH_StatusTypeDef Status = USBH_FAIL;
  AUDIO_HandleTypeDef *AUDIO_Handle; 
  
  if(phost->gState == HOST_CLASS)
  {
    AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
    if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY)
    {
      if(AUDIO_Handle->headphone.buf <= buf)
      {
        AUDIO_Handle->headphone.cbuf = buf;
        if ( AUDIO_Handle->headphone.buf == buf)
        {
          AUDIO_Handle->headphone.partial_ptr = 0;
        }
        Status = USBH_OK;  
      }    
    }
  }
  return Status;  
}

/**
  * @brief  USBH_AUDIO_SetControlAttribute 
  *         Set Control Attribute
  * @param  phost: Host handle
  * @param  attrib: control attribute
  * @retval USBH Status
  */
static USBH_StatusTypeDef USBH_AUDIO_SetControlAttribute (USBH_HandleTypeDef *phost, uint8_t attrib)
{
  USBH_StatusTypeDef status = USBH_BUSY ;
  AUDIO_HandleTypeDef *AUDIO_Handle; 

  
  AUDIO_Handle = (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  switch (attrib)
  {
  case 0x01:
    AUDIO_Handle->headphone.attribute.volume += AUDIO_Handle->headphone.attribute.resolution;
    break;
    
  case 0x02:
    AUDIO_Handle->headphone.attribute.volume -= AUDIO_Handle->headphone.attribute.resolution; 
    break;
    
  }
  
  if(AUDIO_Handle->headphone.attribute.volume > AUDIO_Handle->headphone.attribute.volumeMax)
  {
    AUDIO_Handle->headphone.attribute.volume =AUDIO_Handle->headphone.attribute.volumeMax;
  }
  
  if(AUDIO_Handle->headphone.attribute.volume < AUDIO_Handle->headphone.attribute.volumeMin)
  {
    AUDIO_Handle->headphone.attribute.volume =AUDIO_Handle->headphone.attribute.volumeMin;
  }
  
  if(AUDIO_SetVolume (phost, 
                                 AUDIO_Handle->temp_feature, 
                                 AUDIO_Handle->temp_channels, 
                                 AUDIO_Handle->headphone.attribute.volume) != USBH_BUSY)
  {
    
    if(AUDIO_Handle->temp_channels ==  1)
    {
      AUDIO_Handle->temp_feature  = AUDIO_Handle->headphone.asociated_feature;
      AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels;
      status = USBH_OK;
    }
    else
    {
      AUDIO_Handle->temp_channels--;
    }
    AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME;
  }
  
 
  return status;
}


/**
  * @brief  USBH_AUDIO_SetVolume 
  *         Set Volume
  * @param  phost: Host handle
  * @param  volume: VOLUME_UP/ VOLUME_DOWN
  * @retval USBH Status
  */
USBH_StatusTypeDef USBH_AUDIO_SetVolume (USBH_HandleTypeDef *phost, AUDIO_VolumeCtrlTypeDef volume_ctl)
{
  AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
  
  if((volume_ctl == VOLUME_UP) || (volume_ctl == VOLUME_DOWN))
  {
    if(phost->gState == HOST_CLASS)
    {
      AUDIO_Handle =  (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;
      if(AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY)
      {
        AUDIO_Handle->control_state = (volume_ctl == VOLUME_UP)? AUDIO_CONTROL_VOLUME_UP : AUDIO_CONTROL_VOLUME_DOWN;
        return USBH_OK;  
      }
    }
  }
  return USBH_FAIL;
}
/**
  * @brief  AUDIO_SetVolume 
  *         Set Volume
  * @param  phost: Host handle
  * @param  feature: feature Unit index
  * @param  channel: channel index
  * @param  volume: new volume
  * @retval USBH Status
  */
static USBH_StatusTypeDef AUDIO_SetVolume (USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel, uint16_t volume)
{
  USBH_StatusTypeDef status = USBH_BUSY ;
  AUDIO_HandleTypeDef *AUDIO_Handle; 

  
  AUDIO_Handle = (AUDIO_HandleTypeDef*) phost->pActiveClass->pData;

  AUDIO_Handle->mem[0] = volume;
  
  status = USBH_AC_SetCur(phost, 
                          UAC_FEATURE_UNIT, 
                          feature,
                          VOLUME_CONTROL,
                          channel,
                          2);
    
  return status; 
}

/**
  * @brief  The function informs user that Settings have been changed
  *  @param  phost: Selected device
  * @retval None
  */
__weak void USBH_AUDIO_FrequencySet(USBH_HandleTypeDef *phost)
{
  
}
                                     
/**
  * @brief  The function informs user that User data are processed
  *  @param  phost: Selected device
  * @retval None
  */
__weak void  USBH_AUDIO_BufferEmptyCallback(USBH_HandleTypeDef *phost)
{
   
}
/**
* @}
*/ 

/**
* @}
*/ 

/**
* @}
*/


/**
* @}
*/


/**
* @}
*/

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
