
/******************************************************************************
*@file  : hal_i2s.c
*@brief : I2S HAL module driver.
******************************************************************************/

#include "hal.h" 

#ifdef HAL_I2S_MODULE_ENABLED

static void HAL_I2S_InitParamter(I2S_HandleTypeDef *hi2s);

/******************************************************************************
*@brief : This function handles I2S interrupt request.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: None
******************************************************************************/
void HAL_I2S_IRQHander(I2S_HandleTypeDef *hi2s)
{
    uint32_t Flag;
    uint32_t ITSource;
    
    if ((hi2s->Init.Mode == I2S_MODE_MASTER_TX) || (hi2s->Init.Mode == I2S_MODE_SLAVE_TX))
    {
        Flag = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_TXUDE | I2S_FLAG_TXE);
        ITSource = __HAL_I2S_GET_IT_SOURCE(hi2s, I2S_IT_ERR | I2S_IT_TXE);
        
        /* Check for errors */
        if (ITSource & I2S_IT_ERR)
        {
            /* tx underload error flag */
            if (Flag & I2S_FLAG_TXUDE)
            {
                hi2s->TxError |= I2S_FLAG_TXUDE;
            }
        }
        
        /* Tx buffer empty interrupt */
        if ((Flag & I2S_FLAG_TXE) && (ITSource & I2S_IT_TXE))
        {
            /* Tx buffer empty callback function */
            HAL_I2S_TxEmptyCallback(hi2s);
        }
            
        if (hi2s->TxError)
        {
            /* Tx Error callback function */
            HAL_I2S_TxErrorCallback(hi2s);
        }
    }
    else
    {
        Flag = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_FE | I2S_FLAG_RXOVE | I2S_FLAG_RXNE);
        ITSource = __HAL_I2S_GET_IT_SOURCE(hi2s, I2S_IT_ERR | I2S_IT_RXNE);
        
        /* Check for errors */
        if (ITSource & I2S_IT_ERR)
        {
            /* frame error flag */
            if (Flag & I2S_FLAG_FE)
            {
                hi2s->RxError |= I2S_FLAG_FE;
            }
            
            /* rx buffer overflow error flag */
            if (Flag & I2S_FLAG_RXOVE)
            {
                hi2s->RxError |= I2S_FLAG_RXOVE;
            }
        }
        
        /* Rx buffer non empty interrupt */
        if ((Flag & I2S_FLAG_RXNE) && (ITSource & I2S_IT_RXNE))
        {
            /* Rx buffer non empty callback function */
            HAL_I2S_RxNonEmptyCallback(hi2s);
        }
            
        if (hi2s->RxError)
        {
            /* Rx Error callback function */
            HAL_I2S_RxErrorCallback(hi2s);
        }
    }
}

/******************************************************************************
*@brief : Initialize the I2S according to the specified.
*         parameters in the I2S_InitTypeDef and create the associated handle.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Init(I2S_HandleTypeDef *hi2s)
{
    uint32_t Freq;
    uint32_t div;
    uint32_t primask;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(IS_I2S_MODE(hi2s->Init.Mode));
    assert_param(IS_I2S_STANDARD(hi2s->Init.Standard));
    assert_param(IS_I2S_DATAFORMAT(hi2s->Init.DataFormat));
    assert_param(IS_I2S_MCLKOUTPUT(hi2s->Init.MCLKOutput));
    assert_param(IS_I2S_CLOCKPOLARITY(hi2s->Init.ClockPolarity));
    
    assert_param(hi2s->Init.AudioFreq != 0u);
    
    Freq = HAL_RCC_GetPCLK1Freq();
    if (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_DISABLE)
    {
        if (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B_EXTENDED_TO_16B)
            Freq = Freq >> 5;
        else
            Freq = Freq >> 6;
    }
    else
    {
        Freq = Freq >> 8;
    }
    assert_param(Freq >= hi2s->Init.AudioFreq);
    div = Freq / hi2s->Init.AudioFreq;
    if (div & 1u)
    {
        div = (div - 1) >> 1;
        assert_param(div <= 0x1FFU);
        div += I2S_PR_OF;
    }
    else
    {
        div = div >> 1;
        assert_param(div <= 0x1FFU);
    }
    
    /* Init the low level hardware : GPIO, CLOCK, CORTEX...etc */
    HAL_I2S_MspInit(hi2s);
    
    /* Disable I2S */
    __HAL_I2S_DISABLE(hi2s);
    
    /* Init the internal parameter */
    HAL_I2S_InitParamter(hi2s);
    
    if (hi2s->Instance == I2S1)
    {
        __HAL_RCC_I2S1_RESET();
    }
    else if (hi2s->Instance == I2S2)
    {
        __HAL_RCC_I2S2_RESET();
    }
    
    hi2s->Instance->CR = hi2s->Init.Mode | hi2s->Init.Standard | hi2s->Init.DataFormat | \
                         hi2s->Init.ClockPolarity;
    
    hi2s->Instance->PR = hi2s->Init.MCLKOutput | div;
    
    primask = __get_PRIMASK();
    __set_PRIMASK(1);
    
    __HAL_I2S_ENABLE(hi2s);
    __HAL_I2S_DISABLE(hi2s);
    
    __set_PRIMASK(primask);
    
    return HAL_OK;
}

/******************************************************************************
*@brief : DeInitialize the I2S peripheral.
*         Before Deinitialization, the sending or receiving process needs to be aborted.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_DeInit(I2S_HandleTypeDef *hi2s)
{
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    
    __HAL_I2S_DISABLE(hi2s);
    
    /* DeInit the low level hardware: GPIO, CLOCK, NVIC... */
    HAL_I2S_MspDeInit(hi2s);
    
    /* Init the internal parameter */
    HAL_I2S_InitParamter(hi2s);
    
    return HAL_OK;
}


/******************************************************************************
*@brief : Initialize the I2S MSP.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: None
******************************************************************************/
__weak void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s)
{
    UNUSED(hi2s);
}

/******************************************************************************
*@brief : DeInitialize the I2S MSP.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: None
******************************************************************************/
__weak void HAL_I2S_MspDeInit(I2S_HandleTypeDef *hi2s)
{
    UNUSED(hi2s);
}

/******************************************************************************
*@brief : Register a User I2S Callback.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@note    The registered callback function must be after initialization.
*@param : id: ID of the callback to be registered
*             This parameter can be a combination of @ref I2S_CallbackID.
*             @arg I2S_CALLBACKID_TXCPLT     : transfer complete callback function.
*             @arg I2S_CALLBACKID_TXHALFCPLT : transfer half complete callback function.
*             @arg I2S_CALLBACKID_TXERROR    : transfer error callback function.
*             @arg I2S_CALLBACKID_RXCPLT     : recevice completion callback function.
*             @arg I2S_CALLBACKID_RXHALFCPLT : recevice half completion callback function.
*             @arg I2S_CALLBACKID_RXERROR    : receive error callback function.
*@param : pCallback: pointer to the Callback function.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_RegisterCallback(I2S_HandleTypeDef *hi2s, uint32_t id, pI2S_CallbackTypeDef pCallback)
{
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(IS_I2S_CALLBACKID(id));
    assert_param(pCallback != NULL);
    
	switch (id)
	{
		case  I2S_CALLBACKID_TXCPLT:
			
			hi2s->TxCpltCallback = pCallback;
			break;

		case  I2S_CALLBACKID_TXHALFCPLT:
			
			hi2s->TxHalfCpltCallback = pCallback;
			break;

		case  I2S_CALLBACKID_TXERROR:
			
			hi2s->TxErrorCallback = pCallback;
			break;

		case  I2S_CALLBACKID_RXCPLT:
			
			hi2s->RxCpltCallback = pCallback;
			break;

		case  I2S_CALLBACKID_RXHALFCPLT:
			
			hi2s->RxHalfCpltCallback = pCallback;
			break;

		case  I2S_CALLBACKID_RXERROR:
			
			hi2s->RxErrorCallback = pCallback;
			break;

		default:
			return HAL_ERROR;
	}
	return HAL_OK;
}


/******************************************************************************
*@brief : Unregister a User I2S Callback.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : id: ID of the callback to be registered
*             This parameter can be a combination of @ref I2S_CallbackID.
*             @arg I2S_CALLBACKID_TXCPLT     : transfer complete callback function.
*             @arg I2S_CALLBACKID_TXHALFCPLT : transfer half complete callback function.
*             @arg I2S_CALLBACKID_TXERROR    : transfer error callback function.
*             @arg I2S_CALLBACKID_RXCPLT     : recevice completion callback function.
*             @arg I2S_CALLBACKID_RXHALFCPLT : recevice half completion callback function.
*             @arg I2S_CALLBACKID_RXERROR    : receive error callback function.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_UnRegisterCallback(I2S_HandleTypeDef *hi2s, uint32_t id)
{
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(IS_I2S_CALLBACKID(id));
    
	switch (id)
	{
		case  I2S_CALLBACKID_TXCPLT:
			
			hi2s->TxCpltCallback = NULL;
			break;

		case  I2S_CALLBACKID_TXHALFCPLT:
			
			hi2s->TxHalfCpltCallback = NULL;
			break;

		case  I2S_CALLBACKID_TXERROR:
			
			hi2s->TxErrorCallback = NULL;
			break;

		case  I2S_CALLBACKID_RXCPLT:
			
			hi2s->RxCpltCallback = NULL;
			break;

		case  I2S_CALLBACKID_RXHALFCPLT:
			
			hi2s->RxHalfCpltCallback = NULL;
			break;

		case  I2S_CALLBACKID_RXERROR:
			
			hi2s->RxErrorCallback = NULL;
			break;

		default:
			return HAL_ERROR;
	}
	return HAL_OK;
}

/******************************************************************************
*@brief : Send an amount of data in blocking mode.
*@note    In blocking mode, the user cannot enable interrupt.
*@note    Users can add or ignore error handling in TxErrorCallback(). 
*         Error codes are stored in hi2s->TxError.
*@note    The last word was being sent when the function exited. 
*         The application also needs to call the HAL_I2S_GetBusyStatus() function 
*         to detect the end of sending.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be sent.
*@param : timeout: Timeout duration.
*                  The minimum value of this parameter is 1.
*                  If the value of this parameter is 0xFFFFFFFF, it will be sent until all data are sent.
*                  @node The timeout should be greater than the time of all data transfers.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Transmit(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint32_t size, uint32_t timeout)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    uint32_t startTick;

    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(IS_I2S_DATAFORMAT(hi2s->Init.DataFormat));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    assert_param(timeout != 0U);
    
    /* If I2S is sending, an error is returned */
    if (hi2s->TxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* Blocking transmission can be aborted. Clear the abort flag bit */
    hi2s->TxAbort  = DISABLE;
    
    /* tx state: busy */
    hi2s->TxState  = I2S_STATE_BUSY;
    
    if (hi2s->Init.Mode == I2S_MODE_MASTER_TX)
    {
        temp = hi2s->Instance->DR;
        temp = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_FE);
    }
    else
    {
        __HAL_I2S_DISABLE(hi2s);
        tempCR = hi2s->Instance->CR;
        tempPR = hi2s->Instance->PR;
        
        if (hi2s->Instance == I2S1)
        {
            __HAL_RCC_I2S1_RESET();
        }
        else if (hi2s->Instance == I2S2)
        {
            __HAL_RCC_I2S2_RESET();
        }
        
        hi2s->Instance->CR = tempCR;
        hi2s->Instance->PR = tempPR;
    }
    
    hi2s->pTxBuf  = pdata;
    hi2s->TxCount = size;
    hi2s->TxError = 0U;
    
    __HAL_I2S_ENABLE(hi2s);
    
    startTick = HAL_GetTick();
    while (1)
    {
        /* Whether sending is aborted */
        if (hi2s->TxAbort != DISABLE)
        {
            if (__HAL_I2S_GET_FLAG(hi2s, (I2S_FLAG_TXE | I2S_FLAG_BUSY)) == I2S_FLAG_TXE)
            {
                hi2s->TxState  = I2S_STATE_READY;
                return (HAL_ERROR);
            }
        }
        else
        {
            /* Check for errors */
            hi2s->TxError = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_FE | I2S_FLAG_TXUDE);
            if (hi2s->TxError)
            {
                if (hi2s->TxErrorCallback)
                {
                    hi2s->TxErrorCallback(hi2s);
                }
                __HAL_I2S_DISABLE(hi2s);
                hi2s->TxState  = I2S_STATE_READY;
                return (HAL_ERROR);
            }
            
            if (hi2s->TxCount)
            {
                if (__HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_TXE))
                {
                    hi2s->Instance->DR = *pdata++;
                    hi2s->TxCount--;
                    if (hi2s->TxCount == 0)
                    {
                        hi2s->TxState  = I2S_STATE_READY;
                        return HAL_OK;
                    }
                }
            }
        }
        
        if (timeout != 0xFFFFFFFF)
        {
            /* Whether the sending time has expired */
            if ((HAL_GetTick() - startTick) >= timeout)
            {
                __HAL_I2S_DISABLE(hi2s);
                hi2s->TxState  = I2S_STATE_READY;
                return (HAL_TIMEOUT);
            }
        }
    }
}

/******************************************************************************
*@brief : Receive an amount of data in blocking mode.
*@note    In blocking mode, the user cannot enable interrupt.
*@note    Users can add or ignore error handling in RxErrorCallback(). 
*         Error codes are stored in hi2s->RxError.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be received.
*@param : timeout: Timeout duration.
*                  If the value of this parameter is 0, the received data will be detected only once and will not wait.
*                  If the value of this parameter is 0xFFFFFFFF, it will be received until all data are received.
*                  @node The timeout should be greater than the time of all data transfers.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Receive(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint32_t size, uint32_t timeout)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    uint32_t Data;
    uint32_t startTick;

    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    
    /* If I2S is receiving, an error is returned */
    if (hi2s->RxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* Blocking reception can be aborted. Clear the abort flag bit */
    hi2s->RxAbort = DISABLE;
    
    /* rx state: busy */
    hi2s->RxState  = I2S_STATE_BUSY;
    
    __HAL_I2S_DISABLE(hi2s);
    tempCR = hi2s->Instance->CR;
    tempPR = hi2s->Instance->PR;
    
    if (hi2s->Instance == I2S1)
    {
        __HAL_RCC_I2S1_RESET();
    }
    else if (hi2s->Instance == I2S2)
    {
        __HAL_RCC_I2S2_RESET();
    }
    
    hi2s->Instance->CR = tempCR;
    hi2s->Instance->PR = tempPR;
    
    hi2s->pRxBuf  = pdata;
    hi2s->RxCount = size;
    
    __HAL_I2S_ENABLE(hi2s);
    
    startTick = HAL_GetTick();
    while (1)
    {
        /* Whether reception is aborted */
        if (hi2s->RxAbort != DISABLE)
        {
            __HAL_I2S_DISABLE(hi2s);
            hi2s->RxState  = I2S_STATE_READY;
            return (HAL_ERROR);
        }
        else
        {
            /* Whether there are errors */
            hi2s->RxError = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_RXOVE | I2S_FLAG_FE);
            if (hi2s->RxError)
            {
                if (hi2s->RxErrorCallback)
                {
                    hi2s->RxErrorCallback(hi2s);
                }
                __HAL_I2S_DISABLE(hi2s);
                hi2s->RxState  = I2S_STATE_READY;
                return (HAL_ERROR);
            }
            
            if (hi2s->RxCount)
            {
                if (__HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_RXNE))
                {
                    /* receive data */
                    *pdata++ = (uint32_t)(hi2s->Instance->DR);
                    hi2s->RxCount--;
                    
                    if (hi2s->RxCount == 0)
                    {
                        __HAL_I2S_DISABLE(hi2s);
                        hi2s->RxState  = I2S_STATE_READY;
                        return HAL_OK;
                    }
                }
            }
        }
        
        /* Whether the receiving time has expired */
        if (timeout != 0xFFFFFFFF)
        {
            if ((HAL_GetTick() - startTick) >= timeout)
            {
                __HAL_I2S_DISABLE(hi2s);
                hi2s->RxState  = I2S_STATE_READY;
                return (HAL_TIMEOUT);
            }
        }
    }
}

/******************************************************************************
*@brief : Send an amount of data in interrupt mode.
*@note    In interrupt mode, the transmission related interrupts (TXE/ERR) are forced enabled.
*@note    Users can add or ignore error handling in ErrorCallback(). 
*         Error codes are stored in hi2s->TxError.
*@note    When the callback function TxCpltCallback() is called, the last word is being sent. 
*         The application also needs to call the HAL_I2S_GetBusyStatus() function 
*         to detect the end of sending.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be received.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Transmit_IT(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint32_t size)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    
    /* If I2S is sending, an error is returned */
    if (hi2s->TxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* tx state: busy */
    hi2s->TxState  = I2S_STATE_BUSY_IT;
    
    if (hi2s->Init.Mode == I2S_MODE_MASTER_TX)
    {
        temp = hi2s->Instance->DR;
        temp = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_FE);
    }
    else
    {
        __HAL_I2S_DISABLE(hi2s);
        tempCR = hi2s->Instance->CR;
        tempPR = hi2s->Instance->PR;
        
        if (hi2s->Instance == I2S1)
        {
            __HAL_RCC_I2S1_RESET();
        }
        else if (hi2s->Instance == I2S2)
        {
            __HAL_RCC_I2S2_RESET();
        }
        
        hi2s->Instance->CR = tempCR;
        hi2s->Instance->PR = tempPR;
    }
    
    hi2s->pTxBuf  = pdata;
    hi2s->TxCount = size;
    hi2s->TxError = 0U;
    
    /* enable interrupt (TXE/ERR) */
    __HAL_I2S_ENABLE_IT(hi2s, I2S_IT_TXE | I2S_IT_ERR);
    
    __HAL_I2S_ENABLE(hi2s);
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Receive an amount of data in interrupt mode.
*@note    In interrupt mode, receiving completion interrupt (RXNE/ERR) are forced enabled.
*         If an error occurs in the reception, the reception stops automatically.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be received.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Receive_IT(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint32_t size)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    
    /* If I2S is receiving, an error is returned */
    if (hi2s->RxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* rx state: busy */
    hi2s->RxState  = I2S_STATE_BUSY_IT;
    
    __HAL_I2S_DISABLE(hi2s);
    tempCR = hi2s->Instance->CR;
    tempPR = hi2s->Instance->PR;
    
    if (hi2s->Instance == I2S1)
    {
        __HAL_RCC_I2S1_RESET();
    }
    else if (hi2s->Instance == I2S2)
    {
        __HAL_RCC_I2S2_RESET();
    }
    
    hi2s->Instance->CR = tempCR;
    hi2s->Instance->PR = tempPR;
    
    hi2s->pRxBuf  = pdata;
    hi2s->RxCount = size;
    hi2s->RxError = 0;
    
    /* enable interrupt (RXNE/ERR) */
    __HAL_I2S_ENABLE_IT(hi2s, I2S_IT_RXNE | I2S_IT_ERR);
    
    __HAL_I2S_ENABLE(hi2s);
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Send an amount of data in DMA mode.
*@note    In DMA mode, the tx buffer empty interrupt (TXE) is forced to disabled.
*         the error interrupt (ERR) is forced to enabled.
*         the DMA transmission related interrupt (ITC/HFTC/IE) is forced to enabled.
*         If an DMA transfer error occurs in the transmission, 
*         the transmission stops automatically.
*@note    When the callback function TxCpltCallback() is called, the last word is being sent. 
*         The application also needs to call the HAL_I2S_GetBusyStatus() function 
*         to detect the end of sending.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be received.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Transmit_DMA(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint16_t size)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    
    /* If I2S is sending, an error is returned */
    if (hi2s->TxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* tx state: busy */
    hi2s->TxState  = I2S_STATE_BUSY_DMA;
    
    if (hi2s->Init.Mode == I2S_MODE_MASTER_TX)
    {
        temp = hi2s->Instance->DR;
        temp = __HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_FE);
    }
    else
    {
        __HAL_I2S_DISABLE(hi2s);
        tempCR = hi2s->Instance->CR;
        tempPR = hi2s->Instance->PR;
        
        if (hi2s->Instance == I2S1)
        {
            __HAL_RCC_I2S1_RESET();
        }
        else if (hi2s->Instance == I2S2)
        {
            __HAL_RCC_I2S2_RESET();
        }
        
        hi2s->Instance->CR = tempCR;
        hi2s->Instance->PR = tempPR;
    }
    
    /* set dma callback function */
    hi2s->hdmatx->XferCpltCallback  = HAL_I2S_DMATxCpltCallback;
    if (hi2s->TxHalfCpltCallback)
        hi2s->hdmatx->XferHalfCpltCallback  = HAL_I2S_DMATxHalfCpltCallback;
    else
        hi2s->hdmatx->XferHalfCpltCallback  = NULL;
    hi2s->hdmatx->XferErrorCallback  = HAL_I2S_DMATxErrorCallback;
    
    hi2s->TxCount = size;
    hi2s->TxError = 0U;
    
    /* Start DMA interrupt transfer */
    if (HAL_DMA_Start_IT(hi2s->hdmatx, (uint32_t)pdata, (uint32_t)&hi2s->Instance->DR, size) != HAL_OK)
    {
        /* DMA sending failed */
        hi2s->TxError = I2S_DMA_TX_ERROR;
        hi2s->TxState  = I2S_STATE_READY;
        return  HAL_ERROR;
    }
    
    if (hi2s->TxErrorCallback)
    {
        __HAL_I2S_ENABLE_IT(hi2s, I2S_IT_ERR);
    }
    
    __HAL_I2S_ENABLE(hi2s);
    
    /* enable I2S Tx DMA */
    __HAL_I2S_ENABLE_DMA_TX(hi2s);
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Receive an amount of data in DMA mode.
*@note    In DMA mode, rx buffer non empty interrupt (RXNE) is forced disable. 
*         the error interrupt (ERR) is forced enabled.
*         the DMA transmission related interrupt (ITC/HFTC/IE) is forced to enabled.
*         If an DMA transfer error occurs in the reception, 
*         the reception stops automatically.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@param : pdata: Pointer to data buffer.
*@param : size: Amount of data elements to be received.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_Receive_DMA(I2S_HandleTypeDef *hi2s, uint32_t *pdata, uint16_t size)
{
    volatile uint32_t temp;
    uint32_t tempCR;
    uint32_t tempPR;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    assert_param(pdata != NULL);
    assert_param(size != 0U);
    
    /* If I2S is receiving, an error is returned */
    if (hi2s->RxState != I2S_STATE_READY)
        return HAL_ERROR;
    
    /* rx state: busy */
    hi2s->RxState  = I2S_STATE_BUSY_DMA;
    
    __HAL_I2S_DISABLE(hi2s);
    tempCR = hi2s->Instance->CR;
    tempPR = hi2s->Instance->PR;
    
    if (hi2s->Instance == I2S1)
    {
        __HAL_RCC_I2S1_RESET();
    }
    else if (hi2s->Instance == I2S2)
    {
        __HAL_RCC_I2S2_RESET();
    }
    
    hi2s->Instance->CR = tempCR;
    hi2s->Instance->PR = tempPR;
    
    
    /* set dma callback function */
    hi2s->hdmarx->XferCpltCallback  = HAL_I2S_DMARxCpltCallback;
    hi2s->hdmarx->XferErrorCallback  = HAL_I2S_DMARxErrorCallback;
    if (hi2s->RxHalfCpltCallback)
        hi2s->hdmarx->XferHalfCpltCallback  = HAL_I2S_DMARxHalfCpltCallback;
    else
        hi2s->hdmarx->XferHalfCpltCallback  = NULL;
    
    hi2s->RxCount = size;
    hi2s->RxError = 0U;
    
    /* Start DMA interrupt transfer */
    if (HAL_DMA_Start_IT(hi2s->hdmarx, (uint32_t)&hi2s->Instance->DR, (uint32_t)pdata, size) != HAL_OK)
    {
        /* Set error code to DMA */
        hi2s->RxError = I2S_DMA_RX_ERROR;
        hi2s->RxState  = I2S_STATE_READY;
        return HAL_ERROR;
    }
    
    /* enable the error interrupt (ERR) */
    if (hi2s->RxErrorCallback)
    {
        __HAL_I2S_ENABLE_IT(hi2s, I2S_IT_ERR);
    }
    
    __HAL_I2S_ENABLE(hi2s);
    
    /* enable I2S Rx DMA */
    __HAL_I2S_ENABLE_DMA_RX(hi2s);
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Abort ongoing transmit transfer(block mode/interrupt mode/dma mode).
*         In blocking mode, check TxState to exit the abort function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_AbortTransmit(I2S_HandleTypeDef *hi2s)
{
    volatile uint32_t temp;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    
    /* If I2S is not sent, return directly */
    if (hi2s->TxState == I2S_STATE_READY)
        return HAL_OK;
    
    if (hi2s->TxState == I2S_STATE_BUSY)
    {
        /* Abort ongoing transmit transfer(in block mode) */
        
        /* enable tx abort flag */
        hi2s->TxAbort = ENABLE;
    }
    else if (hi2s->TxState == I2S_STATE_BUSY_IT)
    {
        /* Abort ongoing transmit transfer(interrupt mode) */
        
        /* disable interrupt (TXE/ERR) */
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_TXE | I2S_IT_ERR);
        
        /* end of sending */
        hi2s->TxState = I2S_STATE_READY;
    }
    else if (hi2s->TxState == I2S_STATE_BUSY_DMA)
    {
        /* Abort ongoing transmit transfer(dma mode) */
        
        /* disable interrupt (ERR) */
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
        
        /* abort dma transfer */
        HAL_DMA_Abort(hi2s->hdmatx);
        
        /* disable I2S Tx DMA */
        __HAL_I2S_DISABLE_DMA_TX(hi2s);
    
        /* Take the number of unsent data */
        hi2s->TxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmatx);
        
        /* end of sending */
        hi2s->TxState = I2S_STATE_READY;
    }
    else
    {
        return HAL_ERROR;
    }
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Abort ongoing reception transfer(block mode/interrupt mode/dma mode).
*         In blocking mode, check TxState to exit the abort function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: HAL status
******************************************************************************/
HAL_StatusTypeDef HAL_I2S_AbortReceive(I2S_HandleTypeDef *hi2s)
{
    volatile uint32_t temp;
    
    /* Check the parameters */
    assert_param(IS_I2S_HANDLE(hi2s));
    assert_param(IS_I2S_INSTANCE(hi2s->Instance));
    
    /* If I2S is not receving, return directly */
    if (hi2s->RxState == I2S_STATE_READY)
        return HAL_OK;
    
    if (hi2s->RxState == I2S_STATE_BUSY)
    {
        /* Abort ongoing transmit transfer(block mode) */
        
        /* enable rx abort flag */
        hi2s->RxAbort = ENABLE;
    }
    else if (hi2s->RxState == I2S_STATE_BUSY_IT)
    {
        /* Abort ongoing transmit transfer(interrupt mode) */
        
        /* disable interrupt (RXNE/ERR) */
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_RXNE | I2S_IT_ERR);
        
        __HAL_I2S_DISABLE(hi2s);
        
        /* End of reception */
        hi2s->RxState = I2S_STATE_READY;
    }
    else if (hi2s->RxState == I2S_STATE_BUSY_DMA)
    {
        /* Abort ongoing transmit transfer(dma mode) */
        
        /* disable interrupt (RXNE/ERR) */
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
        
        /* abort dma transfer */
        HAL_DMA_Abort(hi2s->hdmarx);
        
        /* disable I2S Rx DMA */
        __HAL_I2S_DISABLE_DMA_RX(hi2s);
    
        __HAL_I2S_DISABLE(hi2s);
        
        /* Take the number of data not received */
        hi2s->RxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmarx);
        
        /* End of reception */
        hi2s->RxState = I2S_STATE_READY;
    }
    else
    {
        return HAL_ERROR;
    }
    
    return HAL_OK;
}

/******************************************************************************
*@brief : Get busy status.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: busy status.
*         The return value can be @ ref FlagStatus value
*         @arg RESET: I2S idle status.
*         @arg SET: I2S busy status.
******************************************************************************/
FlagStatus HAL_I2S_GetBusyStatus(I2S_HandleTypeDef *hi2s)
{
    assert_param(IS_I2S_HANDLE(hi2s));
    
    if (__HAL_I2S_GET_FLAG(hi2s, I2S_FLAG_BUSY))
        return (SET);
    else
        return (RESET);
}

/******************************************************************************
*@brief : Get sending status.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: sending status.
*         The return value can be @ ref I2S_State value
*         @arg I2S_STATE_READY: I2S not sent.
*         @arg I2S_STATE_BUSY: I2S sending(block mode).
*         @arg I2S_STATE_BUSY_IT: I2S sending(interrupt mode).
*         @arg I2S_STATE_BUSY_DMA: I2S sending(dma mode).
******************************************************************************/
uint32_t HAL_I2S_GetTxState(I2S_HandleTypeDef *hi2s)
{
    assert_param(IS_I2S_HANDLE(hi2s));
    
    return (hi2s->TxState);
}

/******************************************************************************
*@brief : Get the receiving status.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: receiving status.
*         The return value can be @ ref I2S_State value
*         @arg I2S_STATE_READY: I2S did not receive.
*         @arg I2S_STATE_BUSY: I2S is receiving(block mode).
*         @arg I2S_STATE_BUSY_IT: I2S is receiving(interrupt mode).
*         @arg I2S_STATE_BUSY_DMA: I2S is receiving(dma mode).
******************************************************************************/
uint32_t HAL_I2S_GetRxState(I2S_HandleTypeDef *hi2s)
{
    assert_param(IS_I2S_HANDLE(hi2s));
    
    return (hi2s->RxState);
}

/******************************************************************************
*@brief : Tx buffer empty callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_TxEmptyCallback(I2S_HandleTypeDef *hi2s)
{
    volatile uint32_t i;
    
    /* Continue sending */
    if (hi2s->TxCount)
    {
        hi2s->Instance->DR = *hi2s->pTxBuf++;
        hi2s->TxCount--;
    }
    else
    {
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_TXE | I2S_IT_ERR);
        
        hi2s->TxState = I2S_STATE_READY;
        
        if (hi2s->TxCpltCallback)
        {
            hi2s->TxCpltCallback(hi2s);
        }
    }
}

/******************************************************************************
*@brief : Tx Error callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_TxErrorCallback(I2S_HandleTypeDef *hi2s)
{
    __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_TXE | I2S_IT_ERR);
    
    hi2s->TxState = I2S_STATE_READY;
    
    if (hi2s->TxErrorCallback)
    {
        hi2s->TxErrorCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Rx buffer non empty callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*               the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_RxNonEmptyCallback(I2S_HandleTypeDef *hi2s)
{
    if (hi2s->RxCount)
    {
        *hi2s->pRxBuf++ = hi2s->Instance->DR;
        hi2s->RxCount--;
        
        if (hi2s->RxCount == 0)
        {
            __HAL_I2S_DISABLE(hi2s);
            
            /* disable interrupt (RXNE/ERR) */
            __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_RXNE | I2S_IT_ERR);
            
            hi2s->RxState = I2S_STATE_READY;
            
            if (hi2s->RxCpltCallback)
            {
                hi2s->RxCpltCallback(hi2s);
            }
        }
    }
}

/******************************************************************************
*@brief : Rx Error callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_RxErrorCallback(I2S_HandleTypeDef *hi2s)
{
    __HAL_I2S_DISABLE(hi2s);
            
    __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_RXNE | I2S_IT_ERR);
    
    hi2s->RxState = I2S_STATE_READY;
    
    if (hi2s->RxErrorCallback)
    {
        hi2s->RxErrorCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Tx DMA transfer complete callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMATxCpltCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    /* Take the number of unsent data */
    hi2s->TxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmatx);
    
    __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
    
    /* tx dma transer complete */
    hi2s->TxState = I2S_STATE_READY;
    
    if (hi2s->TxCpltCallback)
    {
        hi2s->TxCpltCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Tx DMA half transfer complete callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMATxHalfCpltCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    /* Take the number of unsent data */
    hi2s->TxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmatx);
    
    if (hi2s->TxHalfCpltCallback)
    {
        hi2s->TxHalfCpltCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Tx DMA transfer error callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMATxErrorCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    /* disable I2S DMA */
    __HAL_I2S_DISABLE_DMA_TX(hi2s);
    
    __HAL_I2S_DISABLE(hi2s);
    
    /* Disable the error interrupt */
    __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
    
    /* abort tx dma transfer */
    HAL_DMA_Abort(hdma);
    
    /* Take the number of unsent data */
    hi2s->TxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmatx);
    
    /* set the dma transfer error */
    hi2s->TxError |= I2S_DMA_TX_ERROR;
    
    hi2s->TxState = I2S_STATE_READY;
    
    if (hi2s->TxErrorCallback)
    {
        hi2s->TxErrorCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Rx DMA transfer complete callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMARxCpltCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    if (hdma->Instance->CHLLI == NULL)
    {
        /* disable I2S Rx DMA */
        __HAL_I2S_DISABLE_DMA_RX(hi2s);
        
        __HAL_I2S_DISABLE(hi2s);
        
        /* disable the error interrupt (ERR) */
        __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
        
        /* Take the number of data not received */
        hi2s->RxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmarx);
        
        /* End of reception */
        hi2s->RxState = I2S_STATE_READY;
    }
    
    if (hi2s->RxCpltCallback)
    {
        hi2s->RxCpltCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Rx DMA hlaf transfer complete callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMARxHalfCpltCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    /* Take the number of data not received */
    hi2s->RxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmarx);
    
    if (hi2s->RxHalfCpltCallback)
    {
        hi2s->RxHalfCpltCallback(hi2s);
    }
}

/******************************************************************************
*@brief : Rx DMA transfer error callback function.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
__weak void HAL_I2S_DMARxErrorCallback(DMA_HandleTypeDef *hdma)
{
    I2S_HandleTypeDef *hi2s;
    
    hi2s = hdma->Parent;
    
    /* disable I2S Rx DMA */
    __HAL_I2S_DISABLE_DMA_RX(hi2s);
    
    __HAL_I2S_DISABLE(hi2s);
    
    /* Disable the error interrupt */
    __HAL_I2S_DISABLE_IT(hi2s, I2S_IT_ERR);
    
    /* abort rx dma transfer */
    HAL_DMA_Abort(hdma);
    
    /* Take the number of data not received */
    hi2s->RxCount = __HAL_DMA_GET_COUNTER(hi2s->hdmarx);
    
    /* set the dma transfer error */
    hi2s->RxError |= I2S_DMA_RX_ERROR;
    
    /* End of reception */
    hi2s->RxState = I2S_STATE_READY;
    
    if (hi2s->RxErrorCallback)
    {
        hi2s->RxErrorCallback(hi2s);
    }
}


/******************************************************************************
*@brief : Init the internal parameter.
*@param : hi2s: pointer to a I2S_HandleTypeDef structure that contains
*                  the configuration information for I2S module.
*@return: None.
******************************************************************************/
static void HAL_I2S_InitParamter(I2S_HandleTypeDef *hi2s)
{
    hi2s->TxState                = 0U;
    hi2s->pTxBuf                 = 0U;
    hi2s->TxCount                = 0U;
    hi2s->TxAbort                = 0U;
    
    hi2s->RxState                = 0U;
    hi2s->RxError                = 0U;
    hi2s->pRxBuf                 = 0U;
    hi2s->RxCount                = 0U;
    hi2s->RxAbort                = 0U;
    
    hi2s->TxCpltCallback         = NULL;
    hi2s->TxHalfCpltCallback     = NULL;
    hi2s->TxErrorCallback        = NULL;
    hi2s->RxCpltCallback         = NULL;
    hi2s->RxHalfCpltCallback     = NULL;
    hi2s->RxErrorCallback        = NULL;
}

#endif /* HAL_I2S_MODULE_ENABLED */

/************************ (C) COPYRIGHT AISINOCHIP *****END OF FILE****/








