/*******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 */
/******************************************************************************/
/** \file sd_card.c
 **
 ** A detailed description is available at
 ** @link SdiocGroup SDIOC description @endlink
 **
 **   - 2018-11-15  CDT First version for Device Driver Library of SDIOC.
 **
 ******************************************************************************/

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "sdmmc_cmd.h"
#include "sd_card.h"

/**
 *******************************************************************************
 ** \addtogroup SdiocGroup
 ******************************************************************************/
//@{

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/
typedef enum en_sd_card_type
{
    SdCardSdsc     = 0u,
    SdCardSdhcSdxc = 1u,
    SdCardSecured  = 3u,
} en_sd_card_type_t;

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*!< Card Command Class supported. */
#define SDMMC_CARD_CCCC_ERASE               (0x00000020u)

/*!< Card locked */
#define SDMMC_CARD_LOCKED                   (0x02000000u)

/*!< Voltage trial times */
#define SD_CARD_MAX_VOLT_TRIAL              (0x0000FFFFu)

/*!< High speed */
#define SD_CHECK_FUNCTION_HIGH_SPEED        (0x00FFFF01u)
#define SD_SET_FUNCTION_HIGH_SPEED          (0x80FFFF01u)

/*!< Block size is 512 bytes */
#define SD_CARD_BLOCK_SIZE                  (512u)

/*!< SDIOC buffer align size */
#define SDIOC_BUF_ALIGN_SIZE                (4ul)

/*!< Log Block Number for 2G bytes Cards */
#define SD_CARD_CAPACITY                    (0x400000u)

/*!< Card power up status bit (busy) */
#define SD_CARD_OCR_BUSY                    (0x80000000ul)

#define SD_CARD_OP_NONE                     (0x00000000u)  /*!< None                             */
#define SD_CARD_OP_READ_SINGLE_BLOCK        (0x00000001u)  /*!< Read single block operation      */
#define SD_CARD_OP_READ_MULTIPLE_BLOCK      (0x00000002u)  /*!< Read multiple blocks operation   */
#define SD_CARD_OP_WRITE_SINGLE_BLOCK       (0x00000010u)  /*!< Write single block operation     */
#define SD_CARD_OP_WRITE_MULTIPLE_BLOCK     (0x00000020u)  /*!< Write multiple blocks operation  */
#define SD_CARD_OP_IT                       (0x00000008u)  /*!< Process in Interrupt mode        */
#define SD_CARD_OP_DMA                      (0x00000080u)  /*!< Process in DMA mode              */

/*!< the SD card relative card address. */
#define RelCardAddress(handle)              ((handle)->stcSdCardInfo.u32RelCardAddr)
#define IsCardProgramming(handle)           ((SdmmcCardStatePgm == (handle)->stcCardStatus.CURRENT_STATE) ? true : false)
#define IsCardReadyForData(handle)          ((1u == (handle)->stcCardStatus.READY_FOR_DATA) ? true : false)

#define IS_DMA_CFG_VALID(handle)                                               \
(   (NULL != (handle)->pstcDmaInitCfg)      &&                                 \
    (NULL != (handle)->pstcDmaInitCfg->DMAx))

/*!< the SD card use DMA unit && channel. */
#define DMA_Unit(handle)                    ((handle)->pstcDmaInitCfg->DMAx)
#define DMA_CH(handle)                      ((handle)->pstcDmaInitCfg->enDmaCh)

/*!< Parameter valid check for buffer address. */
#define IS_VALID_TRANSFER_BUF_ALIGN(x)      (!((SDIOC_BUF_ALIGN_SIZE-1ul) & ((uint32_t)(x))))

/*!< Parameter valid check for SDIOC command value. */
#define IS_VALID_TRANSFER_BUF_LEN(x)        (!((SD_CARD_BLOCK_SIZE - 1u) & (x)))

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/
static en_result_t SdCardInitSd(stc_sd_handle_t *handle);
static en_result_t SdCardPowerON(stc_sd_handle_t *handle);
static en_result_t SdCardInitHost(const stc_sd_handle_t *handle);
static en_result_t SdCardSetSpeed(stc_sd_handle_t *handle);
static en_result_t SdCardSetBusWidth(stc_sd_handle_t *handle);
static en_result_t SdCardCheckReayForData(stc_sd_handle_t *handle,
                                uint32_t u32Timeout);
static en_result_t DmaSdiocTxConfig(M4_DMA_TypeDef* DMAx,
                                en_dma_channel_t enCh,
                                M4_SDIOC_TypeDef *SDIOCx,
                                uint8_t *pu8TxBuf,
                                uint16_t u16len);
static en_result_t DmaSdiocRxConfig(M4_DMA_TypeDef* DMAx,
                                en_dma_channel_t enCh,
                                M4_SDIOC_TypeDef *SDIOCx,
                                uint8_t *pu8RxBuf,
                                uint16_t u16len);

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/

/**
 *******************************************************************************
 ** \brief Initialize SD.
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] pstcInitCfg             The pointer of SD configure structure
 ** \arg This parameter detail refer @ref stc_sdcard_init_t
 **
 ** \retval Ok                          Initialize SD successfully.
 ** \retval Error                       Initialize SD unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following cases matches:
 **                                     - handle == NULL
 **                                     - pstcInitCfg == NULL
 **
 ******************************************************************************/
en_result_t SDCARD_Init(stc_sd_handle_t *handle,
                                const stc_sdcard_init_t *pstcInitCfg)
{
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != handle) && (NULL != pstcInitCfg))
    {
        handle->pstcCardInitCfg = pstcInitCfg;

        enRet = SdCardInitHost(handle);
        if (enRet != Ok)
        {
            return enRet;
        }

        enRet = SdCardPowerON(handle);
        if (enRet != Ok)
        {
            return enRet;
        }

        enRet = SdCardInitSd(handle);
        if (enRet != Ok)
        {
            return enRet;
        }

        enRet = SdCardSetBusWidth(handle);
        if (enRet != Ok)
        {
            return enRet;
        }

        enRet = SdCardSetSpeed(handle);
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Set SD Card device Read/Write mode.
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] enDevMode               Read/Write mode
 ** \arg SdCardDmaMode                  DMA mode transfer
 ** \arg SdCardPollingMode              Polling mode transfer
 **
 ** \retval Ok                          Set successfully.
 ** \retval ErrorInvalidParameter       handle pointer is NULL.
 **
 ******************************************************************************/
en_result_t SDCARD_SetDeviceMode(stc_sd_handle_t *handle,
                                en_sd_card_device_mode_t enDevMode)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != handle)
    {
        handle->enDevMode = enDevMode;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Get SD Card device Read/Write mode.
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval SdCardDmaMode               DMA mode transfer
 ** \retval SdCardPollingMode           Polling mode transfer
 **
 ******************************************************************************/
en_sd_card_device_mode_t SDCARD_GetDeviceMode(const stc_sd_handle_t *handle)
{
    DDL_ASSERT(NULL != handle);

    return handle->enDevMode;
}

/**
 *******************************************************************************
 ** \brief Get Card specified Data information.
 **
 ** \param [in] handle                  Pointer to SD Card handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          Get successfully.
 ** \retval ErrorInvalidParameter       If one of following cases matches:
 **                                     - handle == NULL
 **
 ******************************************************************************/
en_result_t SDCARD_GetCardCSD(stc_sd_handle_t *handle)
{
    uint32_t u32Csize = 0ul;
    uint32_t u32NumSector = 0ul;
    uint32_t u32CsizeMulti = 0ul;
    stc_sdcard_csd_v1_t *pstcCsdSd = NULL;
    stc_sdcard_csd_v2_t *pstcCsdSdHc = NULL;
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != handle)
    {
        /* High Capacity  CSD Version 2.0*/
        if ((handle->CSD[3] & 0x00FF0000ul) == 0x00400000ul)
        {
            pstcCsdSdHc = (stc_sdcard_csd_v2_t *)&handle->CSD[0];
            u32Csize = ((unsigned int)pstcCsdSdHc->C_SIZE3 << 16ul)
                     + ((unsigned int)pstcCsdSdHc->C_SIZE2 << 8ul)
                     + pstcCsdSdHc->C_SIZE1;

            u32NumSector = (u32Csize + 1ul) << 10ul;

            handle->stcSdCardInfo.u32Class = (((uint32_t)pstcCsdSdHc->CCC2) << 4u) | ((uint32_t)pstcCsdSdHc->CCC1);
            handle->stcSdCardInfo.u32BlockSize = 1ul << (pstcCsdSdHc->READ_BL_LEN);
        }
        else  /* Standard Capacity     CSD Version 1.xx */
        {
            pstcCsdSd = (stc_sdcard_csd_v1_t *)&handle->CSD[0];

            u32Csize = ((unsigned int)pstcCsdSd->C_SIZE3 << 10)
                       + ((unsigned int)pstcCsdSd->C_SIZE2 << 2)
                       + pstcCsdSd->C_SIZE1;

            u32CsizeMulti = ((uint32_t)(pstcCsdSd->C_SIZE_MULTI2) << 1) + (uint32_t)pstcCsdSd->C_SIZE_MULTI1;
            u32NumSector = (u32Csize + 1ul) << (u32CsizeMulti + 2ul);

            if (pstcCsdSd->READ_BL_LEN == 0x0Au)
            {
                u32NumSector *= 2ul;
            }
            else if (pstcCsdSd->READ_BL_LEN == 0x0Bu)
            {
                u32NumSector *= 4ul;
            }
            else
            {
                /* Do nothing: only avoid MISRA warning */
            }

            handle->stcSdCardInfo.u32Class = ((uint32_t)pstcCsdSd->CCC2 << 4) | (uint32_t)pstcCsdSd->CCC1;
            handle->stcSdCardInfo.u32BlockSize = 1ul << (pstcCsdSd->READ_BL_LEN);
        }

        handle->stcSdCardInfo.u32BlockNbr = u32NumSector;
        handle->stcSdCardInfo.u32LogBlockNbr = (handle->stcSdCardInfo.u32BlockNbr) * ((handle->stcSdCardInfo.u32BlockSize) / SD_CARD_BLOCK_SIZE);
        handle->stcSdCardInfo.u32LogBlockSize = SD_CARD_BLOCK_SIZE;
        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Erase SD card blocks.
 **
 ** \param [in] handle                  Pointer to SD Card handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] u32BlkStartAddr         Block start address
 ** \param [in] u32BlkEndAddr           Block end address
 ** \param [in] u32Timeout              Erase timeout value
 **
 ** \retval Ok                          Erase successfully.
 ** \retval Error                       Erase unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following cases matches:
 **                                     - handle == NULL
 **                                     - u32BlkStartAddr out of range
 **                                     - u32BlkEndAddr out of range
 **
 ******************************************************************************/
en_result_t SDCARD_Erase(stc_sd_handle_t *handle,
                                uint32_t u32BlkStartAddr,
                                uint32_t u32BlkEndAddr,
                                uint32_t u32Timeout)
{
    en_result_t enCmdRet = Error;
    __IO uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 8u / 1000u);

    if ((NULL == handle)                     ||
        (u32BlkStartAddr > u32BlkEndAddr)    ||
        (u32BlkEndAddr > handle->stcSdCardInfo.u32BlockNbr))
    {
        return ErrorInvalidParameter;
    }

    /* Check if the card command class supports erase command */
    if (!(handle->stcSdCardInfo.u32Class & SDMMC_CARD_CCCC_ERASE))
    {
        return ErrorAccessRights;
    }

    if ((SDIOC_GetResponse(handle->SDIOCx, SdiocRegResp01) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
    {
        return ErrorAccessRights;
    }

    /* Check the Card capacity in term of Logical number of blocks */
    if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc)
    {
        u32BlkStartAddr *= SD_CARD_BLOCK_SIZE;
        u32BlkEndAddr   *= SD_CARD_BLOCK_SIZE;
    }

    /* Send CMD35 ERASE_GRP_START with argument as address */
    enCmdRet = SDMMC_Cmd32_EraseWrBlkStart(handle->SDIOCx, u32BlkStartAddr, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    /* Send CMD36 ERASE_GRP_END with argument as address */
    enCmdRet = SDMMC_Cmd33_EraseWrBlkEnd(handle->SDIOCx, u32BlkEndAddr, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    enCmdRet = SDMMC_Cmd38_Erase(handle->SDIOCx, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    enCmdRet = SdCardCheckReayForData(handle, u32TimeCount);

    return enCmdRet;
}

/**
 *******************************************************************************
 ** \brief Read block data from SD card
 **
 ** \param [in] handle                  Pointer to SD Card handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] u32BlockAddr            Block address
 ** \param [in] u16BlockCnt             Block Count
 ** \param [in] pu8Data                 Pointer to buffer which will store SD Card data.
 ** \param [in] u32Timeout              Transfer timeout
 **
 ** \retval Ok                          Read successfully.
 ** \retval Error                       Read unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following cases matches:
 **                                     - handle == NULL
 **                                     - pu8Data == NULL
 **                                     - u16BlockCnt == 0
 **                                     - (u32BlockAddr + u16BlockCnt) out of range
 **
 ******************************************************************************/
en_result_t SDCARD_ReadBlocks(stc_sd_handle_t *handle,
                                uint32_t u32BlockAddr,
                                uint16_t u16BlockCnt,
                                uint8_t *pu8Data,
                                uint32_t u32Timeout)
{
    en_result_t enCmdRet;
    stc_sdioc_data_cfg_t stcDataCfg;
    uint8_t *pu8TempBuf = (uint8_t *)pu8Data;
    uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 8u / 1000u);
    __IO uint32_t u32Count = u32TimeCount;
    en_sd_card_device_mode_t enDeviceMode = SDCARD_GetDeviceMode(handle);
    en_flag_status_t enStatus = Reset;
    en_flag_status_t enIrqFlag = Reset;

    if (NULL == handle)
    {
        return ErrorInvalidParameter;
    }

    if ((NULL == pu8Data) || (0u == u16BlockCnt))
    {
        handle->u32ErrorCode |= SD_CARD_ERROR_PARAM;
        return ErrorInvalidParameter;
    }

    handle->u32ErrorCode = SD_CARD_ERROR_NONE;

    if ((u32BlockAddr + u16BlockCnt) > (handle->stcSdCardInfo.u32LogBlockNbr))
    {
        handle->u32ErrorCode |= SD_CARD_ERROR_ADDR_OUT_OF_RANGE;
        return ErrorInvalidParameter;
    }

    if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc)
    {
        u32BlockAddr *= SD_CARD_BLOCK_SIZE;
    }

    /* Set Block Size for Card */
    enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, SD_CARD_BLOCK_SIZE, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    stcDataCfg.u16BlkCnt = u16BlockCnt;
    stcDataCfg.u16BlkSize = SD_CARD_BLOCK_SIZE;
    stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27;
    stcDataCfg.enTransferDir = SdiocTransferToHost;
    stcDataCfg.enAutoCmd12Enable = (u16BlockCnt > 1u) ? Enable:Disable;
    stcDataCfg.enTransferMode = (u16BlockCnt > 1u) ? SdiocTransferMultiple:SdiocTransferSingle;
    enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg);
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    if (enDeviceMode == SdCardDmaMode)
    {
        if (IS_DMA_CFG_VALID(handle))
        {
            DmaSdiocRxConfig(DMA_Unit(handle), DMA_CH(handle), handle->SDIOCx, pu8TempBuf, u16BlockCnt * SD_CARD_BLOCK_SIZE);
        }
    }

    /* Read block(s) in polling mode */
    if (1u == u16BlockCnt)
    {
        handle->Context = SD_CARD_OP_READ_SINGLE_BLOCK;
        /* Read Single Block command */
        enCmdRet = SDMMC_Cmd17_ReadSingleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus));
    }
    else
    {
        handle->Context = SD_CARD_OP_READ_MULTIPLE_BLOCK;
        /* Read Multi Block command */
        enCmdRet = SDMMC_Cmd18_ReadMultipleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus));
    }

    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    if (SdCardPollingMode == enDeviceMode)
    {
        /* Poll on SDIO flags */
        while (u16BlockCnt)
        {
            enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferReadEnble);
            enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferReadReady);
            if ((Set == enStatus) && (Set == enIrqFlag))
            {
                SDIOC_ReadBuffer(handle->SDIOCx, pu8TempBuf, SD_CARD_BLOCK_SIZE);

                u16BlockCnt--;
                u32Count = u32TimeCount;
                pu8TempBuf += SD_CARD_BLOCK_SIZE;
            }

            if (0ul == u32Count--)
            {
                return ErrorTimeout;
            }
        }
    }

    for (u32Count = u32TimeCount; u32Count > 0ul; u32Count--)
    {
        if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete))
        {
            break;
        }
    }

    if (0ul == u32Count)
    {
        return ErrorTimeout;
    }
    else
    {
        SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete);
    }

    /* check whether Data transfer stops */
    if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt))
    {
        enCmdRet = Error;
    }

    return enCmdRet;
}

/**
 *******************************************************************************
 ** \brief Write block data to SD card
 **
 ** \param [in] handle                  Pointer to SD Card handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] u32BlockAddr            Block address
 ** \param [in] u16BlockCnt             Block Count
 ** \param [in] pu8Data                 Pointer to buffer which contains data to be send to SD Card.
 ** \param [in] u32Timeout              Transfer timeout
 **
 ** \retval Ok                          Write successfully.
 ** \retval Error                       Write unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following cases matches:
 **                                     - handle == NULL
 **                                     - pu8Data == NULL
 **                                     - u16BlockCnt == 0
 **                                     - (u32BlockAddr + u16BlockCnt) out of range
 **
 ******************************************************************************/
en_result_t SDCARD_WriteBlocks(stc_sd_handle_t *handle,
                                uint32_t u32BlockAddr,
                                uint16_t u16BlockCnt,
                                uint8_t *pu8Data,
                                uint32_t u32Timeout)
{
    en_result_t enCmdRet;
    stc_sdioc_data_cfg_t stcDataCfg;
    uint8_t *pu8TempBuf = (uint8_t *)pu8Data;
    uint32_t u32TimeCount = u32Timeout * (SystemCoreClock / 50ul / 1000ul);
    __IO uint32_t u32Count = u32TimeCount;
    en_sd_card_device_mode_t enDeviceMode = SDCARD_GetDeviceMode(handle);
    en_flag_status_t enStatus = Reset;
    en_flag_status_t enIrqFlag = Reset;

    if (NULL == handle)
    {
        return ErrorInvalidParameter;
    }

    if ((NULL == pu8Data) || (0u == u16BlockCnt))
    {
        handle->u32ErrorCode |= SD_CARD_ERROR_PARAM;
        return ErrorInvalidParameter;
    }

    handle->u32ErrorCode = SD_CARD_ERROR_NONE;

    if ((u32BlockAddr + u16BlockCnt) > (handle->stcSdCardInfo.u32LogBlockNbr))
    {
        handle->u32ErrorCode |= SD_CARD_ERROR_ADDR_OUT_OF_RANGE;
        return ErrorInvalidParameter;
    }

    /* Set Block Size for Card */
    enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, SD_CARD_BLOCK_SIZE, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    stcDataCfg.u16BlkCnt = u16BlockCnt;
    stcDataCfg.u16BlkSize = SD_CARD_BLOCK_SIZE;
    stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27;
    stcDataCfg.enTransferDir = SdiocTransferToCard;
    stcDataCfg.enAutoCmd12Enable = (u16BlockCnt > 1u) ? Enable:Disable;
    stcDataCfg.enTransferMode = (u16BlockCnt > 1u) ? SdiocTransferMultiple:SdiocTransferSingle;
    enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg);
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    if (handle->stcSdCardInfo.u32CardType != SdCardSdhcSdxc)
    {
        u32BlockAddr *= SD_CARD_BLOCK_SIZE;
    }

    if (enDeviceMode == SdCardDmaMode)
    {
        if (IS_DMA_CFG_VALID(handle))
        {
            DmaSdiocTxConfig(DMA_Unit(handle), DMA_CH(handle), handle->SDIOCx, pu8TempBuf, u16BlockCnt * SD_CARD_BLOCK_SIZE);
        }
    }

    /* Write block(s) in polling mode */
    if (1u == u16BlockCnt)
    {
        /* Read Single Block command */
        enCmdRet = SDMMC_Cmd24_WriteSingleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus));
    }
    else
    {
        /* Read Multi Block command */
        enCmdRet = SDMMC_Cmd25_WriteMultipleBlock(handle->SDIOCx, u32BlockAddr, (uint32_t *)(&handle->stcCardStatus));
    }

    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    if (SdCardPollingMode == enDeviceMode)
    {
        /* Poll on SDIO flags */
        while (u16BlockCnt)
        {
            enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferWriteEnble);
            enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferWriteReady);
            if ((Set == enStatus) && (Set == enIrqFlag))
            {
                /* Write data to SDIO Tx Buffer */
                SDIOC_WriteBuffer(handle->SDIOCx, pu8TempBuf, SD_CARD_BLOCK_SIZE);

                u16BlockCnt--;
                u32Count = u32TimeCount;
                pu8TempBuf += SD_CARD_BLOCK_SIZE;
            }

            if (u32Count-- == 0ul)
            {
                return ErrorTimeout;
            }
        }
    }

    for (u32Count = u32TimeCount; u32Count > 0ul; u32Count--)
    {
        enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocData0PinLvl);
        enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete);
        if ((Set == enStatus) && (Set == enIrqFlag))
        {
            break;
        }
    }

    if (0u == u32Count)
    {
        return ErrorTimeout;
    }
    else
    {
        SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete);
    }

    enCmdRet = SdCardCheckReayForData(handle, u32TimeCount);
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    /* check whether Data transfer stops */
    if(Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt))
    {
        return Error;
    }

    SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocBufferWriteReady);

    return Ok;
}

/**
 ******************************************************************************
 ** \brief Set SD bus width.
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          Set SD bus width successfully.
 ** \retval Error                       Set SD bus width unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following conditions matches:
 **                                     - NULL == pstcCfg
 **                                     - enBusWidth is invalid.
 **                                     - Other invalid configuration
 **
 ******************************************************************************/
static en_result_t SdCardSetBusWidth(stc_sd_handle_t *handle)
{
    uint32_t u32CmdArg = 0ul;
    en_result_t enCmdRet = Ok;

    if ((NULL == handle) || (NULL == handle->pstcCardInitCfg))
    {
        return ErrorInvalidParameter;
    }

    switch (handle->pstcCardInitCfg->enBusWidth)
    {
        case SdiocBusWidth1Bit:
            u32CmdArg = 0u;
            break;
        case SdiocBusWidth4Bit:
            u32CmdArg = 2u;
            break;
        default:
            enCmdRet = ErrorInvalidParameter;
            break;
    }

    if (Ok != enCmdRet)
    {
        return enCmdRet;
    }

    enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, (RelCardAddress(handle) << 16u), (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    enCmdRet = SDMMC_Acmd6_SetBusWidth(handle->SDIOCx, u32CmdArg, (uint32_t *)(&handle->stcCardStatus));
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    enCmdRet = SDIOC_SetBusWidth(handle->SDIOCx, handle->pstcCardInitCfg->enBusWidth);
    if (enCmdRet != Ok)
    {
        return enCmdRet;
    }

    return Ok;
}

/**
 ******************************************************************************
 ** \brief Set SD high speed mode.
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          Set SD bus width successfully.
 ** \retval Ok                          Set SD bus width unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following conditions matches:
 **                                     - NULL == pstcCfg
 **                                     - u32Freq is invalid.
 **
 ******************************************************************************/
static en_result_t SdCardSetSpeed(stc_sd_handle_t *handle)
{
    uint8_t au8TempBuf[64] = {0};
    uint32_t u32TimeCount = 2000 * (SystemCoreClock / 8u / 1000u);
    __IO uint32_t u32Count = u32TimeCount;
    en_flag_status_t enStatus = Reset;
    en_flag_status_t enIrqFlag = Reset;

    en_result_t enCmdRet = ErrorInvalidParameter;
    stc_sdioc_data_cfg_t stcDataCfg;
    uint32_t u32Arg = SD_SET_FUNCTION_HIGH_SPEED;

    if ((NULL != handle) && (NULL != handle->pstcCardInitCfg))
    {
        enCmdRet = Ok;
        if (SdiocHighSpeedMode == handle->pstcCardInitCfg->enSpeedMode)
        {
            /* Set Block Size for Card */
            enCmdRet = SDMMC_Cmd16_SetBlockLength(handle->SDIOCx, 64u, (uint32_t *)(&handle->stcCardStatus));
            if (enCmdRet == Ok)
            {
                stcDataCfg.u16BlkCnt = 1u;
                stcDataCfg.u16BlkSize = 64u;
                stcDataCfg.enDataTimeOut = SdiocDtoSdclk_2_27;
                stcDataCfg.enTransferDir = SdiocTransferToHost;
                stcDataCfg.enAutoCmd12Enable = Disable;
                stcDataCfg.enTransferMode = SdiocTransferSingle;
                enCmdRet = SDIOC_ConfigData(handle->SDIOCx, &stcDataCfg);
                if (enCmdRet == Ok)
                {
                    enCmdRet = SDMMC_Cmd6_SwitchFunc(handle->SDIOCx, u32Arg, (uint32_t *)(&handle->stcCardStatus));
                    if (enCmdRet == Ok)
                    {
                        while (u32Count--)
                        {
                            enStatus = SDIOC_GetStatus(handle->SDIOCx, SdiocBufferReadEnble);
                            enIrqFlag = SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocBufferReadReady);
                            if ((Set == enStatus) && (Set == enIrqFlag))
                            {
                                SDIOC_ReadBuffer(handle->SDIOCx, au8TempBuf, sizeof(au8TempBuf));
                                break;
                            }
                        }

                        if (0ul == u32Count)
                        {
                            return ErrorTimeout;
                        }

                        u32Count = u32TimeCount;
                        while (u32Count--)
                        {
                            if (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete))
                            {
                                SDIOC_ClearNormalIrqFlag(handle->SDIOCx, SdiocTransferComplete);
                                break;
                            }
                        }

                        if (0ul == u32Count)
                        {
                            return ErrorTimeout;
                        }

                        /* Check whether switch function with error or transfer error occur */
                        if ((!(au8TempBuf[16] & 0x01)) ||
                            (Set == SDIOC_GetNormalIrqFlag(handle->SDIOCx, SdiocErrorInt)))
                        {
                            enCmdRet = Error;
                        }
                    }
                }
            }
        }

        if (enCmdRet == Ok)
        {
            SDIOC_SetSpeedMode(handle->SDIOCx, handle->pstcCardInitCfg->enSpeedMode);

            enCmdRet = SDIOC_SetClk(handle->SDIOCx, handle->pstcCardInitCfg->enClkFreq);
        }
    }

    return enCmdRet;
}

/**
 *******************************************************************************
 ** \brief Initialize SDIO Host
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          SDIO host initialized successfully
 ** \retval ErrorInvalidParameter       If one of following conditions matches:
 **                                     - NULL == pstcInitCfg
 **                                     - Other invalid configuration
 **
 ******************************************************************************/
static en_result_t SdCardInitHost(const stc_sd_handle_t *handle)
{
    en_result_t enRet = Ok;

    if ((NULL == handle) || (NULL == handle->pstcCardInitCfg))
    {
        return ErrorInvalidParameter;
    }

    /* Enable SD clock supply */
    if (M4_SDIOC1 == handle->SDIOCx)
    {
        PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SDIOC1, Enable);
    }
    else if (M4_SDIOC2 == handle->SDIOCx)
    {
        PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SDIOC2, Enable);
    }
    else
    {
        return ErrorInvalidParameter;
    }

    /* Set mode: SD */
    SDIOC_SetMode(handle->SDIOCx, SdiocModeSD);

    enRet = SDIOC_Init(handle->SDIOCx, handle->pstcCardInitCfg->pstcInitCfg);

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Initialize SD bus
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          SDIO bus cmd operate successfully.
 ** \retval Error                       SDIO bus cmd operate unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following conditions matches:
 **                                     - NULL == pstcCfg
 **                                     - Other invalid configuration
 **
 ******************************************************************************/
static en_result_t SdCardInitSd(stc_sd_handle_t *handle)
{
    en_result_t enCmdRet = Ok;

    if(NULL == handle)
    {
        return ErrorInvalidParameter;
    }

    if (handle->stcSdCardInfo.u32CardType != SdCardSecured)
    {
        /* Send CMD2 ALL_SEND_CID */
        enCmdRet = SDMMC_Cmd2_AllSendCID(handle->SDIOCx, handle->CID);
        if (enCmdRet != Ok)
        {
            return enCmdRet;
        }

        /* Send CMD3 SET_REL_ADDR with argument 0 */
        /* SD Card publishes its RCA. */
        enCmdRet = SDMMC_Cmd3_SendRelativeAddr(handle->SDIOCx, &(handle->RCA));
        if (enCmdRet != Ok)
        {
            return enCmdRet;
        }

        /* Get the SD card RCA */
        RelCardAddress(handle) = handle->RCA;

        /* Send CMD9 SEND_CSD with argument as card's RCA */
        enCmdRet = SDMMC_Cmd9_SendCSD(handle->SDIOCx, RelCardAddress(handle), handle->CSD);
        if (enCmdRet != Ok)
        {
            return enCmdRet;
        }
    }

    /* Get CSD parameters */
    enCmdRet = SDCARD_GetCardCSD(handle);
    if (enCmdRet == Ok)
    {
        /* Select the Card */
        enCmdRet = SDMMC_Cmd7_SelectDeselectCard(handle->SDIOCx, RelCardAddress(handle), (uint32_t *)(&handle->stcCardStatus));
    }

    return enCmdRet;
}

/**
 *******************************************************************************
 ** \brief Activate SD in power-on stage
 **
 ** \param [in] handle                  Pointer to SD handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 **
 ** \retval Ok                          Activate successfully.
 ** \retval Error                       Activate unsuccessfully.
 ** \retval ErrorInvalidParameter       If one of following conditions matches:
 **                                     - NULL == handle
 **                                     - Other invalid configuration
 **
 ******************************************************************************/
static en_result_t SdCardPowerON(stc_sd_handle_t *handle)
{
    uint32_t u32IfCond = 0u;
    __IO uint32_t u32Count = 0ul;
    en_result_t enCmdRet = Error;

    if (NULL == handle)
    {
        return ErrorInvalidParameter;
    }

    handle->u32ErrorCode = SD_CARD_ERROR_NONE;

    /* CMD0: GO_IDLE_STATE */
    enCmdRet = SDMMC_Cmd0_GoIdleState(handle->SDIOCx);
    if (enCmdRet != Ok)
    {
        handle->u32ErrorCode |= SD_CARD_ERROR_GENERAL_UNKNOWN_ERR;
        return enCmdRet;
    }

    /* Reset OCR valude */
    handle->OCR = 0ul;

    /* CMD8: SEND_IF_COND: Command available only on V2.0 cards */
    enCmdRet = SDMMC_Cmd8_SendIfCond(handle->SDIOCx, &u32IfCond);
    if (enCmdRet != Ok)
    {
        handle->stcSdCardInfo.u32CardVersion = SdCardVer1x;

        /* Send ACMD41 SD_APP_OP_COND with Argument 0x00100000 */
        while (SD_CARD_OCR_BUSY != (handle->OCR & SD_CARD_OCR_BUSY))
        {
            if (SD_CARD_MAX_VOLT_TRIAL == u32Count++)
            {
                handle->u32ErrorCode |= SD_CARD_ERROR_INVALID_VOLTRANGE;
                return enCmdRet;
            }

            /* SEND CMD55 APP_CMD with RCA as 0 */
            enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, 0u, (uint32_t *)(&handle->stcCardStatus));
            if (enCmdRet != Ok)
            {
                continue;
            }

            /* Send CMD41 */
            enCmdRet = SDMMC_Acmd41_SdSendOpCond(handle->SDIOCx, SdmmcStanderdCapacity, &handle->OCR);
            if (enCmdRet == Error)
            {
                handle->u32ErrorCode |= SD_CARD_ERROR_UNSUPPORTED_FEATURE;
                return enCmdRet;
            }
        }

        /* Card type is SDSC */
        handle->stcSdCardInfo.u32CardType = SdCardSdsc;
    }
    else
    {
        handle->stcSdCardInfo.u32CardVersion = SdCardVer2x;

        /* Send ACMD41 SD_APP_OP_COND with Argument 0x40100000 */
        while (SD_CARD_OCR_BUSY != (handle->OCR & SD_CARD_OCR_BUSY))
        {
            if (SD_CARD_MAX_VOLT_TRIAL == u32Count++)
            {
                handle->u32ErrorCode |= SD_CARD_ERROR_INVALID_VOLTRANGE;
                return Error;
            }

            /* SEND CMD55 APP_CMD with RCA as 0 */
            enCmdRet = SDMMC_Cmd55_AppCmd(handle->SDIOCx, 0u, (uint32_t *)(&handle->stcCardStatus));
            if (enCmdRet != Ok)
            {
                continue;
            }

            /* Send CMD41 */
            enCmdRet = SDMMC_Acmd41_SdSendOpCond(handle->SDIOCx, SdmmcHighCapacity, &handle->OCR);
            if (enCmdRet == Error)
            {
                handle->u32ErrorCode |= SD_CARD_ERROR_UNSUPPORTED_FEATURE;
                return enCmdRet;
            }
        }

        if ((handle->OCR & SdmmcHighCapacity) == SdmmcHighCapacity)
        {
            handle->stcSdCardInfo.u32CardType = SdCardSdhcSdxc;
        }
        else
        {
            handle->stcSdCardInfo.u32CardType = SdCardSdsc;
        }
    }

    return Ok;
}

/**
 *******************************************************************************
 ** \brief Checks if the SD card is ready for data.
 **
 ** \param [in] handle                  Pointer to SD Card handle
 ** \arg This parameter detail refer @ref stc_sd_handle_t
 ** \param [in] u32Timeout              Retry to get card status time
 **
 ** \retval Ok                          Card is ready for data
 ** \retval Error                       Card is not ready for data
 ** \retval ErrorInvalidParameter       NULL == handle
 **
 ******************************************************************************/
static en_result_t SdCardCheckReayForData(stc_sd_handle_t *handle, uint32_t u32Timeout)
{
    en_result_t enRet = ErrorInvalidParameter;

    if (NULL != handle)
    {
        do
        {
            enRet = SDMMC_Cmd13_SendStatus(handle->SDIOCx, RelCardAddress(handle), (uint32_t *)(&handle->stcCardStatus));
            if (Ok != enRet)
            {
                break;
            }
        } while ((--u32Timeout) && (false == IsCardReadyForData(handle)));

        if (0u == u32Timeout)
        {
            enRet = ErrorTimeout;
        }
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Configures the DMA Channel for SDIO Tx request.
 **
 ** \param [in] DMAx                    The pointer to DMAC register base
 ** \arg M4_DMA1                        DMAC unit 1 instance register base
 ** \arg M4_DMA2                        DMAC unit 2 instance register base
 ** \param [in] enCh                    The specified DMAC channel.
 ** \arg DmaCh0                         DMAC channel 0
 ** \arg DmaCh1                         DMAC channel 1
 ** \arg DmaCh2                         DMAC channel 2
 ** \arg DmaCh3                         DMAC channel 3
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu8TxBuf                Pointer to the Tx buffer
 ** \param [in] u16len                  Tx buffer size
 **
 ** \retval Ok                          Set successfully.
 ** \retval ErrorInvalidParameter       Set unsuccessfully.
 **
 ******************************************************************************/
static en_result_t DmaSdiocTxConfig(M4_DMA_TypeDef* DMAx,
                                en_dma_channel_t enCh,
                                M4_SDIOC_TypeDef *SDIOCx,
                                uint8_t *pu8TxBuf,
                                uint16_t u16len)
{
    stc_dma_config_t stcDmaInit;
    uint32_t u32Fcg0Periph = (M4_DMA1 == DMAx) ? PWC_FCG0_PERIPH_DMA1 : PWC_FCG0_PERIPH_DMA2;
    en_event_src_t enEvtSrc = (M4_SDIOC1 == SDIOCx) ? EVT_SDIOC1_DMAW : EVT_SDIOC2_DMAW;
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != pu8TxBuf)                      && \
        (IS_VALID_TRANSFER_BUF_LEN(u16len))     && \
        (IS_VALID_TRANSFER_BUF_ALIGN(pu8TxBuf)))
    {
        /* Enable peripheral clock */
        PWC_Fcg0PeriphClockCmd(u32Fcg0Periph, Enable);

        /* Enable DMA. */
        DMA_Cmd(DMAx, Enable);

        /* Initialize DMA. */
        MEM_ZERO_STRUCT(stcDmaInit);
        stcDmaInit.u16BlockSize = SD_CARD_BLOCK_SIZE/4u;        /* Set data block size. */
        stcDmaInit.u16TransferCnt = u16len/SD_CARD_BLOCK_SIZE;  /* Set transfer count. */
        stcDmaInit.u32SrcAddr = (uint32_t)(&pu8TxBuf[0]);       /* Set source address. */
        stcDmaInit.u32DesAddr = (uint32_t)(&SDIOCx->BUF0);      /* Set destination address. */
        stcDmaInit.stcDmaChCfg.enSrcInc = AddressIncrease;      /* Set source address mode. */
        stcDmaInit.stcDmaChCfg.enDesInc = AddressFix;           /* Set destination address mode. */
        stcDmaInit.stcDmaChCfg.enTrnWidth = Dma32Bit;           /* Set data width 8bit. */
        DMA_InitChannel(DMAx, enCh, &stcDmaInit);

        /* Enable the specified DMA channel. */
        DMA_ChannelCmd(DMAx, enCh, Enable);

        /* Clear DMA flag. */
        DMA_ClearIrqFlag(DMAx, enCh, TrnCpltIrq);
        DMA_ClearIrqFlag(DMAx, enCh, BlkTrnCpltIrq);

        /* Enable peripheral circuit trigger function. */
        PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);

        /* Set DMA trigger source. */
        DMA_SetTriggerSrc(DMAx, enCh, enEvtSrc);

        enRet = Ok;
    }

    return enRet;
}

/**
 *******************************************************************************
 ** \brief Configures the DMA Channel for SDIO Rx request.
 **
 ** \param [in] DMAx                    The pointer to DMAC register base
 ** \arg M4_DMA1                        DMAC unit 1 instance register base
 ** \arg M4_DMA2                        DMAC unit 2 instance register base
 ** \param [in] enCh                    The specified DMAC channel.
 ** \arg DmaCh0                         DMAC channel 0
 ** \arg DmaCh1                         DMAC channel 1
 ** \arg DmaCh2                         DMAC channel 2
 ** \arg DmaCh3                         DMAC channel 3
 ** \param [in] SDIOCx                  Pointer to SDIOC instance register base
 ** \arg M4_SDIOC1                      SDIOC unit 1 instance register base
 ** \arg M4_SDIOC2                      SDIOC unit 2 instance register base
 ** \param [in] pu8RxBuf                Pointer to the Rx buffer
 ** \param [in] u16len                  Rx buffer size
 **
 ** \retval Ok                          Set successfully.
 ** \retval ErrorInvalidParameter       Set unsuccessfully.
 **
 ******************************************************************************/
static en_result_t DmaSdiocRxConfig(M4_DMA_TypeDef* DMAx,
                                en_dma_channel_t enCh,
                                M4_SDIOC_TypeDef *SDIOCx,
                                uint8_t *pu8RxBuf,
                                uint16_t u16len)
{
    stc_dma_config_t stcDmaInit;
    uint32_t u32Fcg0Periph = (M4_DMA1 == DMAx) ? PWC_FCG0_PERIPH_DMA1 : PWC_FCG0_PERIPH_DMA2;
    en_event_src_t enEvtSrc = (M4_SDIOC1 == SDIOCx) ? EVT_SDIOC1_DMAR : EVT_SDIOC2_DMAR;
    en_result_t enRet = ErrorInvalidParameter;

    if ((NULL != pu8RxBuf)                      && \
        (IS_VALID_TRANSFER_BUF_LEN(u16len))     && \
        (IS_VALID_TRANSFER_BUF_ALIGN(pu8RxBuf)))
    {
        /* Enable peripheral clock */
        PWC_Fcg0PeriphClockCmd(u32Fcg0Periph,Enable);

        /* Enable DMA. */
        DMA_Cmd(DMAx, Enable);

        /* Initialize DMA. */
        MEM_ZERO_STRUCT(stcDmaInit);
        stcDmaInit.u16BlockSize = SD_CARD_BLOCK_SIZE/4u;        /* Set data block size. */
        stcDmaInit.u16TransferCnt = u16len/SD_CARD_BLOCK_SIZE;  /* Set transfer count. */
        stcDmaInit.u32SrcAddr = (uint32_t)(&SDIOCx->BUF0);      /* Set source address. */
        stcDmaInit.u32DesAddr = (uint32_t)(&pu8RxBuf[0]);       /* Set destination address. */
        stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix;           /* Set source address mode. */
        stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease;      /* Set destination address mode. */
        stcDmaInit.stcDmaChCfg.enTrnWidth = Dma32Bit;           /* Set data width 8bit. */
        DMA_InitChannel(DMAx, enCh, &stcDmaInit);

        /* Enable the specified DMA channel. */
        DMA_ChannelCmd(DMAx, enCh, Enable);

        /* Clear DMA flag. */
        DMA_ClearIrqFlag(DMAx, enCh, TrnCpltIrq);
        DMA_ClearIrqFlag(DMAx, enCh, BlkTrnCpltIrq);

        /* Enable peripheral circuit trigger function. */
        PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);

        /* Set DMA trigger source. */
        DMA_SetTriggerSrc(DMAx, enCh, enEvtSrc);

        enRet = Ok;
    }

    return enRet;
}

/**
 * @brief  Get the current card status.
 * @param  [in] handle                  Pointer to a @ref stc_sd_handle_t structure
 * @param  [out] pu32CardStatus         Pointer to the value of current card status
 * @retval int32_t:
 *           - LL_OK: Get card status success
 *           - LL_ERR: Refer to u32ErrorCode for the reason of error
 *           - LL_ERR_INVD_PARAM: An invalid parameter was write to the send command
 *           - LL_ERR_TIMEOUT: Send command timeout
 */
static en_result_t SD_GetCurrCardStatus(stc_sd_handle_t *handle, uint32_t *pu32CardStatus)
{
    en_result_t i32Ret;

    /* Send Status command */
    i32Ret = SDMMC_Cmd13_SendStatus(handle->SDIOCx, (uint32_t)(handle->stcSdCardInfo.u32RelCardAddr), (uint32_t *)&handle->u32ErrorCode);
    if (Ok != i32Ret) {
        return i32Ret;
    }
    /* Get SD card status */
    *pu32CardStatus = SDIOC_GetResponse(handle->SDIOCx, SdiocRegResp01);

    return i32Ret;
}

/**
 * @brief  Get the current sd card state.
 * @param  [in] handle                  Pointer to a @ref stc_sd_handle_t structure
 * @param  [out] peCardState            Pointer to a @ref en_sd_card_state_t enumeration
 * @retval int32_t:
 *           - LL_OK: Get sd card state success
 *           - LL_ERR: Refer to u32ErrorCode for the reason of error
 *           - LL_ERR_INVD_PARAM: handle == NULL or peCardState == NULL or
 *                                An invalid parameter was write to the send command
 *           - LL_ERR_TIMEOUT: Send command timeout
 */
en_result_t SD_GetCardState(stc_sd_handle_t *handle, en_sd_card_state_t *peCardState)
{
    en_result_t i32Ret;
    uint32_t u32Response = 0UL;

    if ((NULL == peCardState) || (NULL == handle)) {
        i32Ret = ErrorInvalidParameter;
    } else {
        i32Ret = SD_GetCurrCardStatus(handle, &u32Response);
        *peCardState = (en_sd_card_state_t)(uint32_t)((u32Response >> 9) & 0x0FU);
    }

    return i32Ret;
}

//@} // SdiocGroup

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
