
/**
 ******************************************************************************
 *
 * @file        MG32x02z_USBD_API.c
 * @brief       USB initial. 
 *
 * @par         Project
 *              MG32x02z
 * @version     V1.01
 * @date        2021/06/01
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2017 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *
 ******************************************************************************* 
 * @par Disclaimer
 * The Demo software is provided "AS IS" without any warranty, either
 * expressed or implied, including, but not limited to, the implied warranties
 * of merchantability and fitness for a particular purpose. The author will
 * not be liable for any special, incidental, consequential or indirect
 * damages due to loss of data or any other reason.
 * These statements agree with the world wide and local dictated laws about
 * authorship and violence against these laws.
 *******************************************************************************
 *******************************************************************************
 */


/* Includes ------------------------------------------------------------------*/
#include "MG32x02z.h"
#include "MG32x02z_USB.h"
#include "MG32x02z_SYS.h"
#include "MG32x02z_USBD_Core.h"
#include "MG32x02z_USBD_Descriptor.h"
#include "MG32x02z_USB_DRV.h"
#include "MG32x02z_USB_Init.h"
#include "MG32x02z_USBD_Audio_Core.h"
#include "MG32x02z_USBD_MGData.h"
#include "MG32x02z_MEM_DRV.h"
#include "MG32x02z_GPIO_DRV.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static const uint8_t  EPn_ID_Table[8]          = { 0 , 1, 2, 3, 4 , 5 , 6 , 7};
static const uint16_t EPn_RXSIZE_TABLE[8]      = {MG_USB_EP0_SRAM_RXSIZE, MG_USB_EP1_SRAM_RXSIZE, MG_USB_EP2_SRAM_RXSIZE, MG_USB_EP3_SRAM_RXSIZE, MG_USB_EP4_SRAM_RXSIZE, MG_USB_EP5_SRAM_RXSIZE, MG_USB_EP6_SRAM_RXSIZE};
static const uint16_t EPn_RXSRAM_TABLE[8]      = {USB_EP0_SRAM_RXADR , USB_EP1_SRAM_RXADR , USB_EP2_SRAM_RXADR , USB_EP3_SRAM_RXADR , USB_EP4_SRAM_RXADR , USB_EP5_SRAM_RXADR , USB_EP6_SRAM_RXADR , USB_EP7_SRAM_RXADR};
static const uint16_t EPn_TXSRAM_TABLE[8]      = {USB_EP0_SRAM_TXADR , USB_EP1_SRAM_TXADR , USB_EP2_SRAM_TXADR , USB_EP3_SRAM_TXADR , USB_EP4_SRAM_TXADR , USB_EP5_SRAM_TXADR , USB_EP6_SRAM_TXADR , USB_EP7_SRAM_TXADR};
static const uint8_t  EPn_InitaStatus_TABLE[8] = {MG_USB_RXTX_EN , MG_USB_EP1_STATUS, MG_USB_EP2_STATUS, MG_USB_EP3_STATUS, MG_USB_EP4_STATUS, MG_USB_EP5_STATUS, MG_USB_EP6_STATUS, MG_USB_EP7_STATUS};

USBCTR_TypeDef  Ep0;

/* Private function prototypes -----------------------------------------------*/
void USB_IRQHandler(void);
void API_USBD_Endpoint0_ReadData( void );
void USB_Get_Status( void );
void USB_Clear_Feature( void );
void USB_Set_Feature( void );
void USB_Set_Configuration( void );
void USB_Get_Descriptor( void );
void USB_Set_Interface( void );
void USB_Get_Interface( void );
void USB_Stardard_Request( void );
void USB_Class_Request( void );
void API_USBD_ControlWrite( void );

/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/



/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_IRQHandler(void)
{
   USB_Int();   
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */  
void USB_Inital(void)
{
    USB_EP_Struct*  USB_EPX;
    uint8_t         USB_InitTmp;
    
    USB_Cmd(ENABLE);
    
    NVIC_DisableIRQ(USB_IRQn);
    USB_ITEA_Cmd(DISABLE);
 
    //=====================================================
    //EP0 config
    
    USB_TriggerEndpointRst(USB_EP0 , EP_RST_TX_RX);
    
    USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);  
    USB_EndpintIT_Config( USB_EP0 , (USB_EP_IE_TXDONE | USB_EP_IE_RXDONE) , ENABLE);
    

    USB_SetEndpointRXDataAddress(USB_EP0, EPn_RXSRAM_TABLE[0]);             
    USB_SetEndpointTXDataAddress(USB_EP0, EPn_TXSRAM_TABLE[0]);                
    
    USB_SetEndpointRXBlock(USB_EP0,64);
    
    //====================================================
    //EP1 ~ EP7 config
    for(USB_InitTmp=1;USB_InitTmp<8;USB_InitTmp++)
    {
        switch(USB_InitTmp)
        {
            case 7:
                    USB_EPX = USB_EP7;
                    break;
            case 6:
                    USB_EPX = USB_EP6;
                    break;
            case 5:
                    USB_EPX = USB_EP5;
                    break;
            case 4:
                    USB_EPX = USB_EP4;
                    break;
            case 3:
                    USB_EPX = USB_EP3;
                    break;
            case 2:
                    USB_EPX = USB_EP2;
                    break;
            case 1:
            default:
                    USB_EPX = USB_EP1;
                    break;
        }
        USB_TriggerEndpointRst(USB_EPX , EP_RST_TX_RX);
        
        USB_SetEndpointStatus(USB_EPX, EP_RX_DISABLE_TX_DISABLE);
        
        USB_EndpintIT_Config( USB_EPX , USB_EP_IE_ALL, DISABLE); 

        USB_SetEndpointRXDataAddress(USB_EPX, EPn_RXSRAM_TABLE[USB_InitTmp]);
        USB_SetEndpointTXDataAddress(USB_EPX, EPn_TXSRAM_TABLE[USB_InitTmp]);   

    }
    
    //===================================================
    //USB LPM Receive Disable
    USB_LPMhandshakeMode_Select(USB_LPM_DISABLE);
    //===================================================
    //USB Interrupt Config
    #if MG_USB_LPM_EN == 1
        USB_IT_Config(( USB_IT_EP0 | USB_IT_BUS | USB_IT_BUS_SUSF | USB_IT_BUS_RSTF | USB_IT_BUS_RSMF | USB_IT_LPM) , ENABLE);
    #else
        USB_IT_Config(( USB_IT_EP0 | USB_IT_BUS | USB_IT_BUS_SUSF | USB_IT_BUS_RSTF | USB_IT_BUS_RSMF) , ENABLE);      
    #endif
    USB_ITEA_Cmd(ENABLE);
    NVIC_EnableIRQ(USB_IRQn);
    
    //====================================================
    //Control Parameter inital.
    Ep0.USBStatus       = USB_STATUS_DEFAULT;
    Ep0.AudioIN_AltSet  = 0;
    Ep0.AudioOUT_AltSet = 0;
    
    //=====================================================
    //USB Connect to Bus (Enable DP pull-up R
    USB_Connect_Cmd(ENABLE);
 
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_ReadEndpointData( USB_EP_Struct* EPX, uint8_t *Buffer , uint32_t RXShift , uint32_t Cnt)
{
    uint8_t  i;
    uint8_t *USB_EP_RAM;
    
    USB_EP_RAM = (uint8_t*)((0x30000000 + EPX->RX.H[0] + RXShift));
    
    for( i = 0; i <Cnt ; i ++)
    {
        Buffer[i] =  USB_EP_RAM[i];
    }
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_WriteEndpointData( USB_EP_Struct* EPX, __I uint8_t *Buffer , uint32_t bTXShift , uint8_t bTXCnt )
{ 
    uint8_t bEPTX_Tmp;
    uint8_t *USB_EP_RAM;
  
    USB_EP_RAM = (uint8_t*)(0x30000000 + EPX->TX.H[0] + bTXShift);
    
    
    for ( bEPTX_Tmp=0 ; bEPTX_Tmp<bTXCnt ; bEPTX_Tmp++ ) 
    {
        USB_EP_RAM[bEPTX_Tmp] = Buffer[bEPTX_Tmp];
    }
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_WriteEndpointConstData( USB_EP_Struct* EPX, uint8_t ConstData , uint32_t cTXShift , uint8_t cTXCnt )
{ 
    uint8_t cEPTX_Tmp;
    uint8_t *USB_EP_RAM;
  
    USB_EP_RAM = (uint8_t*)(0x30000000 + EPX->TX.H[0] + cTXShift);
    
    for ( cEPTX_Tmp=0 ; cEPTX_Tmp < cTXCnt ; cEPTX_Tmp++ ) 
    {
        USB_EP_RAM[cEPTX_Tmp] = ConstData;
    }
}    
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_WriteEndpointSingleData( USB_EP_Struct* EPX, uint8_t sData , uint8_t shift)
{
    uint8_t *USB_EP_RAM;
    
    USB_EP_RAM = (uint8_t*)(0x30000000 + EPX->TX.H[0]);
    
    USB_EP_RAM[shift] = sData;
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void API_USBD_Endpoint0_ReadData( void )
{ 
    uint16_t BLen, tmp;
    
    //=============================================================================
    tmp = __DRV_USB_GETSETUP_STATUS();
    if ( tmp & ( USB_EP0_STOVW | USB_EP0_EDOVW ))                                              // Setup Token
    { 
        Ep0.DataStage = SETUPSTAGE;
        while(1)
        { 
            tmp = __DRV_USB_GETSETUP_STATUS();
            
            do{ 
                tmp = __DRV_USB_GETSETUP_STATUS();
            }while(tmp & USB_EP0_STOVW);                                                       // waiting STOVE = 0

            do{                                        
                tmp = __DRV_USB_GETSETUP_STATUS();         
            }while(!( tmp & USB_EP0_EDOVW ));                                                  // waiting EDOVW = 1                                        


            __DRV_USB_CLEAR_EDOVW();
            USB_ClearEndpointFlag(USB_EP0,USB_EP_FLAG_RXDONE);

            BLen = USB_GetEndpointRXSize(USB_EP0);
                                         
            USB_ReadEndpointData(USB_EP0 , Ep0.RxTx , 0 , BLen);

            tmp = __DRV_USB_GETSETUP_STATUS();
            if (!(tmp & ( USB_EP0_STOVW | USB_EP0_EDOVW )))
                break;
        }

        USB_SetEndpointStatus(USB_EP0, EP_RX_VALID_TX_NAK);
        __DRV_USB_CLEAR_RXSETUP();

        Ep0.All = BLen;                                                                        // Only for analytic "Urd"
        
        USB_SetEndpointStatus(USB_EP0, EP_RX_VALID); 
    }
    //=============================================================================
    else if( Ep0.DataStage == AUDIO_CONTROL)
    {
        USB_Audio_Control_SetRequest();
    }  
    //=============================================================================
    else if( Ep0.DataStage == AUDIO_ENDPOINT)
    {
        USB_Audio_Endpoint_SetRequest();
    }     
    //=============================================================================
    //Set Report Relationship
    else if( Ep0.DataStage == DFU_RESET)
    {
        USB_SetEndpointTXSize(USB_EP0, 0);
        USB_SetEndpointStatus(USB_EP0, EP_TX_VALID); 
    }
    else if( Ep0.DataStage == DATASTAGE_SRP)
    {
        USB_MGCTR_SetFeatureHandle();
    }
    //=============================================================================
    else                                                                                      // Zero Length Data in RXCNT
    {
        Ep0.DataStage = STATUSSTAGE;
        USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
        
    }                              
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_Get_Status( void )
{ 
    uint8_t   tmp;
    uint16_t  USB_Status;
     
    Ep0.All = 2;                                                                                                          // Only 2 byte transfer to the host
                                                                                                                          
    Ep0.RxTx[1] = 0;                                                                                                      
    switch( Ep0.RxTx[0] & 0x03 )                                                                                          // Request Type ( Reserve low 2 bit )
    {                                                                                                                     
        case DEVICEREQUEST:                                                                                               
                              if ( (Ep0.USBStatus & USB_STATUS_RWEN_MASK) == USB_STATUS_RWEN_ENABLE )                     // Check Remote wakeup enabled or not
                              {                                                                                           
                                   Ep0.RxTx[0] = 0x02;                                                                    // Return Function Remove Wake-up Enable
                              }                                                                                           
                              else                                                                                        
                              {                                                                                           
                                   Ep0.RxTx[0] = 0x00;                                                                    // Return Function Remove Wake-up Disable
                                                                                                      
                              }
                              break;                                                                     
        case ENDPOINTREQUEST:  
                              tmp = (Ep0.RxTx[4] & 0x0F);

                              switch(tmp)
                              {
                                  case 1:
                                          USB_Status = USB_GetEndpointStatus(USB_EP1);
                                          break;
                                  case 2:
                                          USB_Status = USB_GetEndpointStatus(USB_EP2);
                                          break;
                                  case 7:
                                          USB_Status = USB_GetEndpointStatus(USB_EP7); 
                                          break;
                                  default:
                                          USB_Status = 0;
                                          break;
                              }
                              if((USB_Status & USB_EP0CR1_TXSTL0_mask_h1) || (USB_Status & USB_EP0CR1_RXSTL0_mask_h1))
                              {
                                  Ep0.RxTx[0] = 0x01;                                                                     // if EndPoint Rx/Tx STAL then set EndPoint Halt
                              }
                              else
                              {
                                  Ep0.RxTx[0] = 0x00;                                                                     // else seting this EndPoint Avaliable for Rx/Tx 
                              }
                              
                              break;
        case INTERFACEREQUEST: 
                              break;
        default:              
                              USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                              break; 
    }
} 


/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */ 
void USB_Clear_Feature( void )
{ 
    switch( Ep0.RxTx[0] & 0x03 )                                                           // Request Type ( Reserve low 2 bit )
    { 
        case DEVICEREQUEST:    if ( Ep0.RxTx[2] == DEVICE_REMOTE_WAKEUP )
                               {
                                   Ep0.USBStatus &= (uint32_t)(~USB_STATUS_RWEN_MASK);
                               }
                               else
                               {   
                                   USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                               }
                               break;                                                       // Disable the Device Remote Wakeup function
        case ENDPOINTREQUEST:  
                               if ( Ep0.RxTx[2] == ENDPOINT_HALT )
                               { 
                                   switch((Ep0.RxTx[4] & 0x0F))
                                   {
                                       case 1:
                                               USB_SetEndpointOUTSequenceBit(USB_EP1,0);
                                               USB_SetEndpointINSequenceBit(USB_EP1,0);
                                               USB_SetEndpointStatus(USB_EP1, EP_RX_DISABLE_TX_NAK);
                                               break;
                                       case 2:
                                               USB_SetEndpointOUTSequenceBit(USB_EP2,0);
                                               USB_SetEndpointINSequenceBit(USB_EP2,0);
                                               USB_SetEndpointStatus(USB_EP2, EP_RX_DISABLE_TX_NAK);
                                               break;
                                       case 7:
                                               USB_SetEndpointOUTSequenceBit(USB_EP2,0);
                                               USB_SetEndpointINSequenceBit(USB_EP2,0);
                                               USB_SetEndpointStatus(USB_EP7, EP_RX_DISABLE_TX_NAK);
                                               break;
                                       default:
                                               break;
                                   }
                               }
                               else
                               { 
                                   USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                               }
                               break;
        case INTERFACEREQUEST: 
                               break;
        default:               
                               USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                               break;
    }
}
/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Set_Feature( void )
{ 

    switch( Ep0.RxTx[0] & 0x03 )                                                                      // Request Type ( Reserve low 2 bit )
    { 
        case DEVICEREQUEST:    
                               if ( Ep0.RxTx[2] == DEVICE_REMOTE_WAKEUP )
                               {
                                   Ep0.USBStatus |= USB_STATUS_RWEN_MASK;
                               }
                               else
                               { 
                                   USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                               }
                               break;                                                                 // Disable the Device Remote Wakeup function
        case ENDPOINTREQUEST:  
                               if ( Ep0.RxTx[2] == ENDPOINT_HALT )
                               { 
                                   switch((Ep0.RxTx[4] & 0x0F))
                                   {
                                       case 1:
                                               USB_SetEndpointStatus(USB_EP1, EP_RX_STALL_TX_STALL);
                                               break;
                                       case 2:
                                               USB_SetEndpointStatus(USB_EP2, EP_RX_STALL_TX_STALL);
                                               break;
                                       case 7:
                                               USB_SetEndpointStatus(USB_EP7, EP_RX_STALL_TX_STALL);
                                               break;
                                       default:
                                               break;
                                   }
                               }
                               else
                               { 
                                   USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                               }
                               break;
        case INTERFACEREQUEST: 
                               break;
        default:               
                               USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL); 
                               break; 
    }
}  

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Get_Descriptor( void )
{ 
    ctype WLen;
  
  
    WLen.W  = 0;                                 // MSB
    Ep0.All = Ep0.RxTx[7];                       // MSB
    Ep0.All <<= 8;
    Ep0.All += Ep0.RxTx[6];                      // LSB
    

    switch( Ep0.RxTx[3] )	
    { 
        case DEVICE_DESCRIPTOR:      
                                      Ep0.Buf = USB_DEVICE_DESCRIPTOR;
                                      WLen.B[0] = Ep0.Buf[0];
                                      break;
        case CONFIGURATION_DESCRIPTOR: 
                                      Ep0.Buf = USB_CONGFIGURATION_DESCRIPTOR;
                                      WLen.B[1] = Ep0.Buf[3];
                                      WLen.B[0] = Ep0.Buf[2];
                                      break;
        case STRING_DESCRIPTOR:        
                                      switch( Ep0.RxTx[2] )
                                      { 
                                          case 0:  
                                                   Ep0.Buf = USB_STRING_DESCRIPTOR;
                                                   break;
                                          case 1:  
                                                   if ( USB_DEVICE_DESCRIPTOR[14] )
                                                   {
                                                       Ep0.Buf = MANUFACTURER_DESCRIPTOR;
                                                   }
                                                   else
                                                   { 
                                                       Ep0.DataStage = STATUSSTAGE;       
                                                   }
                                                   break;
                                          case 2:  
                                                   if ( USB_DEVICE_DESCRIPTOR[15] )
                                                   {
                                                       Ep0.Buf = PRODUCT_DESCRIPTOR;
                                                   }
                                                   else
                                                   { 
                                                       Ep0.DataStage = STATUSSTAGE;
                                                   }
                                                   break;
                                          case 3:  
                                                   if ( USB_DEVICE_DESCRIPTOR[16] )
                                                   {
                                                       Ep0.Buf = SERIALNUMBER_DESCRIPTOR;
                                                   }
                                                   else
                                                   {  
                                                       Ep0.DataStage = STATUSSTAGE;
                                                   }
                                                   break;
                                          default: 
                                                   Ep0.DataStage = STATUSSTAGE;
                                                   break;
                                    }
                                    WLen.B[0] = Ep0.Buf[0];
                                    break;
        case HID_DESCRIPTOR:           
                                    switch( Ep0.RxTx[4])
                                    {
                                        case 0:
                                                Ep0.Buf = &USB_CONGFIGURATION_DESCRIPTOR[(USB_CONFIGURATION_DESC_SIZE+USB_INTERFACE_DESC_SIZE)];
                                                break;
                                        case 4:
                                                Ep0.Buf = &USB_CONGFIGURATION_DESCRIPTOR[237];
                                                break;
                                        default:
                                                Ep0.DataStage = STATUSSTAGE;
                                                break;                                            
                                    }
                                    WLen.B[0] = Ep0.Buf[0];
                                    break;
        case HID_REPORT:               
                                    switch( Ep0.RxTx[4])    
                                    {
                                        case 0:
                                                Ep0.Buf   = USB_HID_REPORT;
                                                WLen.B[0] = 21;
                                                break;
                                        case 4:
                                                Ep0.Buf   = USB_HID_REPORT2;
                                                WLen.B[0] = 66;
                                                break;
                                        default:
                                                Ep0.DataStage = STATUSSTAGE;
                                                break;
                                    }
    	                            
    	                            break;
    	default:                      
                                    USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL); 
                                    break;
    }
  
    if ( Ep0.All > WLen.W )
        Ep0.All = WLen.W;
  
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
static uint32_t USB_EP_TABLE[2][8] = {{USB_EP0_Base , USB_EP1_Base , USB_EP2_Base , USB_EP3_Base , USB_EP4_Base , USB_EP5_Base , USB_EP6_Base , USB_EP7_Base},
                                      {USB_IT_EP0   , USB_IT_EP1   , USB_IT_EP2   , USB_IT_EP3   , USB_IT_EP4   , USB_IT_EP5   , USB_IT_EP6   , USB_IT_EP7  }};

                               
void USB_Set_Configuration( void )
{ 
    USB_EP_Struct*  USB_EPX;
    uint8_t         USBConfig_Tmp;
    
    Ep0.Tmp = Ep0.RxTx[2];
    if ( Ep0.Tmp )
    { 
        for( USBConfig_Tmp = 1; USBConfig_Tmp < 8; USBConfig_Tmp ++)
        {
            USB_EPX = ((USB_EP_Struct*) USB_EP_TABLE[0][USBConfig_Tmp]);                                        
            
            USB_SetEndpointAddress(USB_EPX, EPn_ID_Table[USBConfig_Tmp] );                                     
            

            USB_IT_Config( USB_EP_TABLE[1][USBConfig_Tmp],ENABLE);
            

            
            switch(EPn_InitaStatus_TABLE[USBConfig_Tmp] )
            {
                case MG_USB_RX_EN:
                                         USB_EndpointDoubleBufferMode_Cmd(USB_EPX,DISABLE);                         
                                         USB_SetEndpointRXBlock(USB_EPX,EPn_RXSIZE_TABLE[USBConfig_Tmp]);  
                                         USB_EndpintIT_Config( USB_EPX , (USB_EP_IE_RXDONE) , ENABLE);                
                                         USB_SetEndpointStatus(USB_EPX , EP_RX_VALID);                               
                                         break;
                case MG_USB_TX_EN:       
                                         USB_EndpointDoubleBufferMode_Cmd(USB_EPX,DISABLE); 
                                         USB_EndpintIT_Config( USB_EPX , (USB_EP_IE_TXDONE) , ENABLE);                
                                         USB_SetEndpointStatus(USB_EPX , EP_TX_NAK);
                                         break;
                case MG_USB_RXTX_EN:     
                                         USB_EndpointDoubleBufferMode_Cmd(USB_EPX,DISABLE);
                                         USB_SetEndpointRXBlock(USB_EPX,EPn_RXSIZE_TABLE[USBConfig_Tmp]);
                                         USB_EndpintIT_Config( USB_EPX , (USB_EP_IE_TXDONE | USB_EP_IE_RXDONE) , ENABLE);
                                         USB_SetEndpointStatus(USB_EPX , EP_RX_VALID_TX_NAK);
                                         break;
                case MG_USB_RX_DB:
                                         USB_EndpointDoubleBufferMode_Cmd(USB_EPX,ENABLE);
                                         USB_SetEndpointRXBlock(USB_EPX,EPn_RXSIZE_TABLE[USBConfig_Tmp]);
                                         USB_EndpintIT_Config( USB_EPX , (USB_EP_IE_DBUFFER_RXDONE) , ENABLE);
                
                                         USB_SetDoubleBufferEndpointStatus(USB_EPX , EP_DB_RXBUFALL_VALID);
                                         break;
                case MG_USB_TX_DB:
                                         USB_EndpointDoubleBufferMode_Cmd(USB_EPX,ENABLE);
                                         USB_EndpintIT_Config( USB_EPX , (USB_EP_IE_DBUFFER_TXDONE) , ENABLE);
                                         USB_SetDoubleBufferEndpointStatus(USB_EPX,EP_DB_TXBUFALL_NAK);
                                         break;
                case MG_USB_RXTX_DISABLE:
                default:
                                         USB_SetEndpointStatus(USB_EPX , EP_RX_DISABLE_TX_DISABLE);
                                         break;
            }

        }
        //=====================================================
        USB_EP1->CR1.MBIT.RXTYPEX = 1;
        USB_EP2->CR1.MBIT.TXTYPEX = 1;
        
        //======================================================
        //LPM Control.
        #if MG_USB_LPM_EN == 1
            USB_LPMhandshakeMode_Select(USB_LPM_ACK);
        #endif
        //======================================================
        //USB enumeration PASS
        Ep0.USBStatus |= USB_STATUS_EMULATION_OK;                                  // Emulation Flow pass
        
        //======================================================
        //Speaker inital
        USB_Audio_Inital();
        
    }
    else                                                                           // Set EP1,2,3,4 into Address state
    { 
        USB_SetEndpointStatus(USB_EP1 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP2 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP3 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP4 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP5 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP6 , EP_RX_DISABLE_TX_DISABLE);
        USB_SetEndpointStatus(USB_EP7 , EP_RX_DISABLE_TX_DISABLE);
    }
}


/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Set_Interface( void )
{ 
    switch( Ep0.RxTx[4] )
    { 
        case 0:
        case 1:
        case 4:
                 if ( Ep0.RxTx[2] > 0 )  
                 {
                     USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                 }
                 else
                 {
                     USB_SetEndpointTXSize(USB_EP0, 0);
                     USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);    
                 }
                 break;
        case 2:           
                 if ( Ep0.RxTx[2] > 1 )  
                 {
                     USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                 }
                 else
                 {
                     Ep0.AudioOUT_AltSet = Ep0.RxTx[2];
                     
                     USB_SetEndpointTXSize(USB_EP0, 0);
                     USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);    
                 }
                 break;
        case 3:
                 if ( Ep0.RxTx[2] > 1 )  
                 {
                     USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);
                 }
                 else
                 {
                     Ep0.AudioIN_AltSet = Ep0.RxTx[2];
                     
                     USB_SetEndpointTXSize(USB_EP0, 0);
                     USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);    
                 }
                 break;
        default: 
                 USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);            // Set Rx/Tx STAL 
                 break;
    }
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Get_Interface( void )
{ 
    Ep0.All = 1;
    switch( Ep0.RxTx[4] )
    { 
        case 0:  
        case 1:
        case 4:
                 Ep0.RxTx[0] = 0;
                 break;            
        case 2:
                 Ep0.RxTx[0] = Ep0.AudioOUT_AltSet;
                 break;
        case 3:
                 Ep0.RxTx[0] = Ep0.AudioIN_AltSet;                                                 
                 break;
        default: 
                 USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);            // Set Rx/Tx STAL 
                 break;
    }
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void API_USBD_ControlRead( void )                                               // Host In , USB Out ( Only for EP0 )
{ 
    uint16_t USB_Delay;
    uint8_t  USB_CtrlRdBLen;

    if ( Ep0.DataStage == DATASTAGE )                                 // In DATASTAGE we should move Data to TXFIFO
    { 
        if ( Ep0.All > EP0_PACKET_SIZE )
        {
            USB_CtrlRdBLen = EP0_PACKET_SIZE;
        }
        else
        {
            USB_CtrlRdBLen = (uint8_t)Ep0.All;
        }
    	
        USB_WriteEndpointData( USB_EP0 , Ep0.Buf , 0 , USB_CtrlRdBLen );

        USB_SetEndpointTXSize(USB_EP0, USB_CtrlRdBLen);
        USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
        Ep0.All -= USB_CtrlRdBLen;                                   // Calculated the Remain Data size
        Ep0.Buf += USB_CtrlRdBLen;                                   // Move Buffer Address in Right position
    }
    else
    { 

        USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);        // Set Rx/Tx STAL 
        
        if ( Ep0.DataStage == SETADDRESS )                           // Different from other STATUSSTAGE(importent)
        { 
            __DRV_USB_SETADDRESS(Ep0.Tmp);
        }
        else if( Ep0.DataStage == DFU_RESET)
        {
            for( USB_Delay = 0; USB_Delay < 60000; USB_Delay++){__NOP();}
                
            SYS->BKP0.W    = 0;
            SYS->BKP0.B[2] = 'D';
            SYS->BKP0.B[1] = 'F';
            SYS->BKP0.B[0] = 'U';
                
            __MEM_UnProtect();    
            
            __MEM_SetBootSelect(MEM_BOOT_ISP);    
            __MEM_Protect();    
            __NVIC_SystemReset();    
        }
    }
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Stardard_Request( void )
{ 
    switch( Ep0.RxTx[1] )                                                                    // Request Code
    { 
        case GET_STATUS:       
                                Ep0.DataStage = DATASTAGE;
                                USB_Get_Status();
                                API_USBD_ControlRead();
                                break;
        case CLEAR_FRATURE:		
                                Ep0.DataStage = STATUSSTAGE;  
                                USB_Clear_Feature();
                                USB_SetEndpointTXSize(USB_EP0, 0);
                                USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
                                break;
        case SET_FEATURE:       
                                Ep0.DataStage = STATUSSTAGE;
                                USB_Set_Feature();
                                USB_SetEndpointTXSize(USB_EP0, 0);
                                USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
                                break;
        case SET_ADDRESS:       
                                Ep0.DataStage = SETADDRESS;                                  // Different from other STATUSSTAGE
                                Ep0.Tmp = Ep0.RxTx[2];
                                USB_SetEndpointTXSize(USB_EP0, 0);
                                USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
                                break;
        case GET_DESCRIPTOR:    
                                Ep0.DataStage = DATASTAGE;
                                USB_Get_Descriptor();
                                API_USBD_ControlRead();
                                break;
        case SET_DESCRIPTOR:    
                                break;
        case GET_CONFIGURATION: 
                                Ep0.DataStage = DATASTAGE;
                                Ep0.RxTx[0] = Ep0.Tmp;                                       // This value get from SET_CONFIGURATION transaction
                                Ep0.All = 1;                                                 // Only 1 byte transfer to the host
                                API_USBD_ControlRead();
                                break;
        case SET_CONFIGURATION: 
                                Ep0.DataStage = STATUSSTAGE;
                                USB_Set_Configuration();                                     // Will store configuration value to Ep0.Tmp
                                USB_SetEndpointTXSize(USB_EP0, 0);
                                USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
                                break;
        case SET_INTERFACE:     
                                Ep0.DataStage = STATUSSTAGE;
                                USB_Set_Interface();
                                break;
        case GET_INTERFACE:     
                                Ep0.DataStage = DATASTAGE;
                                USB_Get_Interface();
                                API_USBD_ControlRead();
                                break;
        case SYNCH_FRAME:       
                                Ep0.DataStage = STATUSSTAGE;
                                USB_SetEndpointTXSize(USB_EP0, 0);
                                USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);                 // USB will return ACK immediately when receive IN transaction
                                Ep0.All = 0;
                                break;
        default:                
                                USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);        // Set Rx/Tx STAL 
                                break;         
    }
}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void USB_Class_Request( void )
{ 
    if( (Ep0.RxTx[0] & USB_AUDIO_REQUEST_TYPE_MASK)==USB_AUDIO_REQUEST_TYPE_ENDPOINT)
    {
        
        USB_Audio_Endpoint_Request();
        
    }
    else
    {
        if( Ep0.RxTx[0] & GET_REQUEST)
        {
            switch( Ep0.RxTx[1])                        
            { 
                case USB_AUDIO_REQUEST_GET_CUR:
                case USB_AUDIO_REQUEST_GET_MIN:
                case USB_AUDIO_REQUEST_GET_MAX:
                case USB_AUDIO_REQUEST_GET_RES:
                                               USB_Audio_Control_Request(GET_REQUEST);
                                               break;
                
                case GET_IDLE:     
                                               Ep0.DataStage = DATASTAGE;
                                               Ep0.RxTx[0]   = USB_Audio.IdleRate;
                                               Ep0.All = 1;
                                               API_USBD_ControlRead();
                                               break;
                case GET_PROTOCOL: 
                                               Ep0.DataStage = DATASTAGE;
                                               Ep0.RxTx[0] = 0;
                                               Ep0.All = 1;
                                               API_USBD_ControlRead();
                                               break;
                case GET_REPORT:   
                                               Ep0.DataStage = DATASTAGE;
                                               USB_MGCTR_GetFeatureHandle();
                                               API_USBD_ControlRead();
                                               break;
                default:           
                                               USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);        // Set Rx/Tx STAL 
                                               break;                
            }
            
            
        }
        else
        {
            switch( Ep0.RxTx[1])                        
            { 
                case USB_AUDIO_REQUEST_SET_CUR:
                case USB_AUDIO_REQUEST_SET_MIN:
                case USB_AUDIO_REQUEST_SET_MAX:
                case USB_AUDIO_REQUEST_SET_RES:
                                               USB_Audio_Control_Request(SET_REQUEST);
                                               break;
                case SET_IDLE:                 
                                               Ep0.DataStage      = STATUSSTAGE;
                                               USB_Audio.IdleRate = Ep0.RxTx[3];
                                               USB_SetEndpointTXSize(USB_EP0, 0);
                                               USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);
                                               break;
                case SET_PROTOCOL:             
                                               Ep0.DataStage = STATUSSTAGE;
                                               USB_SetEndpointTXSize(USB_EP0, 0);
                                               USB_SetEndpointStatus(USB_EP0, EP_TX_VALID);
                                               break;
                case SET_REPORT:               
                                               Ep0.DataStage = DATASTAGE_SRP;
                                               Ep0.All = Ep0.RxTx[7];
                                               Ep0.All <<= 8;
                                               Ep0.All += Ep0.RxTx[6];
                                               break;
                default:                       
                                               Ep0.DataStage = STATUSSTAGE;
                                               USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);        // Set Rx/Tx STAL 
                                               break;                
            }
            
        }
    }

}

/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */
void API_USBD_ControlWrite( void )                                                        // Host Out , USB In ( Only for EPO )
{                                                                                                                                                                       
    Ep0.Buf = Ep0.RxTx;                                                                   // Move Buffer address to RxTx[8] array , Use for API_USBD_ControlRead();
    API_USBD_Endpoint0_ReadData();                                                        // Move Rx Data to RxTxBuf buffer
    
    if ( Ep0.DataStage == SETUPSTAGE )                                                        
    {	                                                                                  
        Ep0.All = 0;                                                                      
        switch( Ep0.RxTx[0] & 0x60 )                                                      // Request Type
        {
            case STANDARD_REQUEST: 
                                   USB_Stardard_Request();
                                   break;
            case CLASS_REQUEST:    
                                   USB_Class_Request();
                                   break;
            default:               
                                   USB_SetEndpointStatus(USB_EP0, EP_RX_STALL_TX_STALL);   // Set Rx/Tx STAL 
                                   break;                       
        }
    }    
}


/**
 *******************************************************************************
 * @brief	    
 * @details     
 * @return      
 * @exception   
 * @note      
 * @par         
 *******************************************************************************
 */

void USB_Int( void )
{ 
    uint32_t        USBIRQ_Status;
    uint32_t        URBEPIRQ_Status1;
    uint8_t         USBIRQ_Tmp;

    //=========================================================================
    //Get USB Interrupt Flag
    USBIRQ_Status       =  USB_GetITAllFlagStatus();

    //=========================================================================
    //USB Bus event handle
    if((USBIRQ_Status & USB_IT_BUS)!=0 )                                                      
    { 
        //---------------------------------------------------------------------
        //Detect Bus Suspend 
        if((USBIRQ_Status & USB_IT_BUS_SUSF)==USB_IT_BUS_SUSF)                                 
        {
            USB_ClearITFlag(USB_IT_BUS_SUSF | USB_IT_BUS);
            
            Ep0.USBStatus |= USB_STATUS_BUS_SUSPEND;
            
            // To do......
        }
        else
        { 
            //---------------------------------------------------------------------
            //Detect Bus Reset.
            if((USBIRQ_Status & USB_IT_BUS_RSTF)==USB_IT_BUS_RSTF)                                
            { 
                USB_ClearITFlag(USB_IT_BUS_RSTF);
                USBIRQ_Tmp = 0x80;

                while( USBIRQ_Tmp  < 228)                            /*!< By changing the parameter (128 ~ 255) to modify Reset debounce. */
                {
                    if(__DRV_USB_GETBUS_STATUS() & USB_BUS_SE0_STA)
                    {
                        USBIRQ_Tmp = USBIRQ_Tmp + 1;
                    }
                    else
                    {
                        USBIRQ_Tmp = 0;
                        break;
                    }                        
                }
                if( USBIRQ_Tmp != 0)
                {
                    USB_Inital();
                    USB_Audio_Inital();
                    
                    Ep0.USBStatus |= USB_STATUS_BUS_RESET;
                    // To do......
                }
            }
            //---------------------------------------------------------------------
            //Detect Bus Resume 
            else if((USBIRQ_Status & USB_IT_BUS_RSMF)==USB_IT_BUS_RSMF)                         
            { 
                USB_ClearITFlag(USB_IT_BUS_RSMF);
                
                Ep0.USBStatus |= USB_STATUS_BUS_RESUME;
                // To do......
            }  
        }
    }
    //=========================================================================
    //ACK response to LPM
    #if MG_USB_LPM_EN == 1
        if( USBIRQ_Status & USB_IT_LPM)
        {
            Ep0.LPM_BLE = USB_GetLPMBESL();
            
            if(USB_GetLPMbRemoteWake()!=0)
            {
                Ep0.USBStatus |= USB_STATUS_RWEN_MASK;
            }
            else
            {
                Ep0.USBStatus &= (~USB_STATUS_RWEN_MASK);
            }
            Ep0.USBStatus = Ep0.USBStatus | USB_STATUS_BUS_SUSPEND;
            
            USB_ClearITFlag(USB_IT_LPM);
        }
    #endif
    //=========================================================================
    //Endpoint handle.
    else  
    {   
        //=====================================================================
        //Endpoint 1 Interrupt handle ( Audio OUT Control)
        if((USBIRQ_Status & USB_IT_EP1)==USB_IT_EP1)
        {
            USB_ClearEndpointFlag(USB_EP1,USB_EP_FLAG_RXDONE);
            
            USB_Audio_OUTData();
        }
        //=====================================================================
        //Endpoint 2 Interrupt handle ( Audio IN Control)
        if((USBIRQ_Status & USB_IT_EP2)==USB_IT_EP2)
        {
            USB_ClearEndpointFlag(USB_EP2 , USB_EP_FLAG_TXDONE);
            USB_Audio_INDataContinuous();
        }
        //=====================================================================
        //Endpoint 7 Interrupt handle ( Audio Multimedia Control)
        if((USBIRQ_Status & USB_IT_EP7)==USB_IT_EP7)
        {
            USB_ClearEndpointFlag(USB_EP7 , USB_EP_FLAG_TXDONE);

            USB_Audio.AudioMultimedia_Busyflag = 0;
        }
        //=====================================================================
        //Endpoint 0 control
        if((USBIRQ_Status & USB_IT_EP0)==USB_IT_EP0)
        {
            URBEPIRQ_Status1 = USB_GetEndpointFlagStatus(USB_EP0);
            
            if((URBEPIRQ_Status1 & USB_EP_FLAG_TXDONE)==USB_EP_FLAG_TXDONE)
            {
                USB_ClearEndpointFlag(USB_EP0,USB_EP_FLAG_TXDONE);
                API_USBD_ControlRead();
            }
            else if((URBEPIRQ_Status1 & USB_EP_FLAG_RXDONE)==USB_EP_FLAG_RXDONE)
            {
                USB_ClearEndpointFlag(USB_EP0,USB_EP_FLAG_RXDONE);
                API_USBD_ControlWrite();
            }
        }
        
    }	  
}











