/**
  ******************************************************************************
  * @file    stm32h7xx_hal_sdio.c
  * @author  MCD Application Team
  * @brief   SDIO HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Secure Digital Input Output (SDIO) peripheral:
  *           + Initialization and de-initialization functions
  *           + IO operation functions
  *           + Peripheral Control functions
  *           + Peripheral State functions
  *
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2017 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  @verbatim
  ==============================================================================
                        ##### How to use this driver #####
  ==============================================================================
  [..]
    This driver implements a high level communication layer for read and write from/to
    this SDIO card. The needed STM32 hardware resources (SDMMC and GPIO) are performed by
    the user in HAL_SDIO_MspInit() function (MSP layer).
    Basically, the MSP layer configuration should be the same as we provide in the
    examples.
    You can easily tailor this configuration according to hardware resources.

  [..]
    This driver is a generic layered driver for SDMMC memories which uses the HAL
    SDMMC driver functions to interface with SDIO cards devices.
    It is used as follows:

    (#)Initialize the SDMMC low level resources by implementing the HAL_SDIO_MspInit() API:
        (##) Enable the SDMMC interface clock using __HAL_RCC_SDMMC_CLK_ENABLE();
        (##) SDMMC pins configuration for SDIO card
            (+++) Enable the clock for the SDMMC GPIOs using the functions __HAL_RCC_GPIOx_CLK_ENABLE();
            (+++) Configure these SDMMC pins as alternate function pull-up using HAL_GPIO_Init()
                  and according to your pin assignment;
        (##) NVIC configuration if you need to use interrupt process (HAL_SDIO_ReadExtended_DMA()
             and HAL_SDIO_WriteExtended_DMA() APIs).
            (+++) Configure the SDMMC interrupt priorities using function HAL_NVIC_SetPriority();
            (+++) Enable the NVIC SDMMC IRQs using function HAL_NVIC_EnableIRQ()
            (+++) SDMMC interrupts are managed using the macros __HAL_SDIO_ENABLE_IT()
                  and __HAL_SDIO_DISABLE_IT() inside the communication process.
            (+++) SDMMC interrupts pending bits are managed using the macros __HAL_SDIO_GET_IT().
        (##) No general propose DMA Configuration is needed, an Internal DMA for SDMMC Peripheral are used.

    (#) At this stage, you can perform SDIO read/write/erase operations after SDIO card initialization.

  *** SDIO Card Initialization and configuration ***
  ================================================
  [..]
    To initialize the SDIO Card, use the HAL_SDIO_Init() function. It Initializes
    SDMMC Peripheral(STM32 side) and the SDIO Card, and put it into StandBy State (Ready for data transfer).
    This function provide the following operations:

    (#) Apply the SDIO Card initialization process at 400KHz. You can change or adapt this
        frequency by adjusting the "ClockDiv" field.
        The SDIO Card frequency (SDMMC_CK) is computed as follows:

           SDMMC_CK = SDMMCCLK / (2 * ClockDiv)

        In initialization mode and according to the SDIO Card standard,
        make sure that the SDMMC_CK frequency doesn't exceed 400KHz.

        This phase of initialization is done through SDMMC_Init() and
        SDMMC_PowerState_ON() SDMMC low level APIs.

    (#) Initialize the SDIO card. The API used is HAL_SDIO_Init().
        This phase allows the card initialization and identification.

    (#) Configure the SDIO Card Data transfer frequency. You can change or adapt this
        frequency by adjusting the "ClockDiv" field by the API HAL_SDIO_ConfigFrequency().

    (#) Configure the SDIO Card in wide bus mode: 4-bits data by the API HAL_SDIO_SetDataBusWidth().

    (#) Configure the SDIO Card data block size by the API : HAL_SDIO_SetBlockSize().

    (#) Configure the SDIO Card speed mode by the API : HAL_SDIO_SetSpeedMode().

    (#) To custumize the SDIO Init card function for the enumeration card sequence, you can register a user callback
        function by calling the HAL_SDIO_RegisterIdentifyCardCallback before the HAL_SDIO_Init() function.

  *** SDIO Card Read operation ***
  ==============================
  [..]
    (+) You can read from SDIO card in polling mode by using function HAL_SDIO_ReadExtended().
        This function support only 2048-bytes block length (the block size should be
        chosen by using the API HAL_SDIO_SetBlockSize).

    (+) You can read from SDIO card in DMA mode by using function HAL_SDIO_ReadExtended_DMA().
        This function support only 2048-bytes block length (the block size should be
        chosen by using the API HAL_SDIO_SetBlockSize).
        After this, you have to ensure that the transfer is done correctly.
        You could also check the DMA transfer process through the SDIO Rx interrupt event.

  *** SDIO Card Write operation ***
  ===============================
  [..]
    (+) You can write to SDIO card in polling mode by using function HAL_SDIO_WriteExtended().
        This function support only 2048-bytes block length (the block size should be
        chosen by using the API HAL_SDIO_SetBlockSize).

    (+) You can write to SDIO card in DMA mode by using function HAL_SDIO_WriteExtended_DMA().
        This function support only 2048-bytes block length (the block size should be
        chosen by using the API HAL_SDIO_SetBlockSize).
        You could also check the DMA transfer process through the SDIO Tx interrupt event.


  *** SDIO card common control register (CCCR) ***
  ======================
  [..]
    (+) The SDIO CCCR allow for quick host checking and control of an IO card's enable and interrupts on a per card and
        per function basis.
        To get the Card common control registers field, you can use the API HAL_SDIO_GetCardCommonControlRegister().

  *** SDIO card Function basic register (FBR) ***
  ===========================
  [..]
    (+) The SDIO card function basic register are used to allow the host to quickly determine the abilities and
        requirements of each function.
    (+) To get the SDIO function basic register information, you can use the API HAL_SDIO_GetCardFBRRegister().

  *** SDIO HAL driver macros list ***
  ==================================
  [..]
    Below the list of most used macros in SDIO HAL driver.

    (+) __HAL_SDIO_ENABLE_IT: Enable the SDIO device interrupt
    (+) __HAL_SDIO_DISABLE_IT: Disable the SDIO device interrupt
    (+) __HAL_SDIO_GET_FLAG: Check whether the specified SDIO flag is set or not
    (+) __HAL_SDIO_CLEAR_FLAG: Clear the SDIO's pending flags
    (+) __HAL_SDIO_GET_IT: Check whether the specified SDIO interrupt has occurred or not
    (+) __HAL_SDIO_GET_IT_SOURCE: Checks whether the specified SDIO interrupt is enabled or not

    (@) You can refer to the SDIO HAL driver header file for more useful macros

  *** Callback registration ***
  =============================================
  [..]
    The compilation define USE_HAL_SDIO_REGISTER_CALLBACKS when set to 1
    allows the user to configure dynamically the driver callbacks.

    Use Functions HAL_SDIO_RegisterCallback() to register a user callback,
    it allows to register following callbacks:
      (+) TxCpltCallback : callback when a transmission transfer is completed.
      (+) RxCpltCallback : callback when a reception transfer is completed.
      (+) ErrorCallback : callback when error occurs.
      (+) MspInitCallback    : SDIO MspInit.
      (+) MspDeInitCallback  : SDIO MspDeInit.
    This function takes as parameters the HAL peripheral handle, the Callback ID
    and a pointer to the user callback function.
    For specific callbacks TransceiverCallback use dedicated register callbacks:
    respectively HAL_SDIO_RegisterTransceiverCallback().

    Use function HAL_SDIO_UnRegisterCallback() to reset a callback to the default
    weak (overridden) function. It allows to reset following callbacks:
      (+) TxCpltCallback : callback when a transmission transfer is completed.
      (+) RxCpltCallback : callback when a reception transfer is completed.
      (+) ErrorCallback : callback when error occurs.
      (+) MspInitCallback    : SDIO MspInit.
      (+) MspDeInitCallback  : SDIO MspDeInit.
    This function) takes as parameters the HAL peripheral handle and the Callback ID.
    For specific callbacks TransceiverCallback use dedicated unregister callbacks:
    respectively HAL_SDIO_UnRegisterTransceiverCallback().

    By default, after the HAL_SDIO_Init and if the state is HAL_SDIO_STATE_RESET
    all callbacks are reset to the corresponding legacy weak (overridden) functions.
    Exception done for MspInit and MspDeInit callbacks that are respectively
    reset to the legacy weak (overridden) functions in the HAL_SDIO_Init
    and HAL_SDIO_DeInit only when these callbacks are null (not registered beforehand).
    If not, MspInit or MspDeInit are not null, the HAL_SDIO_Init and HAL_SDIO_DeInit
    keep and use the user MspInit/MspDeInit callbacks (registered beforehand)

    Callbacks can be registered/unregistered in READY state only.
    Exception done for MspInit/MspDeInit callbacks that can be registered/unregistered
    in READY or RESET state, thus registered (user) MspInit/DeInit callbacks can be used
    during the Init/DeInit.
    In that case first register the MspInit/MspDeInit user callbacks
    using HAL_SDIO_RegisterCallback before calling HAL_SDIO_DeInit
    or HAL_SDIO_Init function.

    When The compilation define USE_HAL_SDIO_REGISTER_CALLBACKS is set to 0 or
    not defined, the callback registering feature is not available
    and weak (overridden) callbacks are used.

  *** SDIO peripheral IO interrupt ***
  =============================================
  [..]
  (+) Below the list of most used SDIO function to check and control the IO card's enable and interrupts on a per
      functions basis.

    (+) HAL_SDIO_EnableIOFunctionInterrupt: Enable SDIO IO interrupt.
    (+) HAL_SDIO_DisableIOFunctionInterrupt: Disable SDIO IO interrupt.
    (+) HAL_SDIO_EnableIOFunction: Enable Function number(0-7)
    (+) HAL_SDIO_DisableIOFunction: Disable Function number(0-7)
    (+) HAL_SDIO_SelectIOFunction: Select a function number(0-7)
    (+) HAL_SDIO_AbortIOFunction: Abort an IO read or write operation and free the SDIO bus.
    (+) HAL_SDIO_EnableIOAsynInterrupt: Enable Bit of asynchronous interrupt
    (+) HAL_SDIO_DisableIOAsynInterrupt: Disable Bit of asynchronous interrupt

  @endverbatim
  ******************************************************************************
  */

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

/** @addtogroup STM32H7xx_HAL_Driver
  * @{
  */

/** @addtogroup SDIO
  * @{
  */
#if defined (SDMMC1) || defined (SDMMC2)
#ifdef HAL_SDIO_MODULE_ENABLED

/* Private define ----------------------------------------------------------------------------------------------------*/
/** @addtogroup SDIO_Private_Defines
  * @{
  */
#define SDIO_INIT_FREQ         400000U /*!< Initialization phase : 400 kHz max */
#define SDIO_TIMEOUT           1000U   /*!< SDIO timeout millisecond           */

#define SDIO_FUNCTION_0        0x00U   /*!< SDIO_Functions 0                   */
#define SDIO_FUNCTION_1        0x01U   /*!< SDIO_Functions 1                   */

#define SDIO_READ              0x0U    /*!< Read flag for cmd52 and cmd53      */
#define SDIO_WRITE             0x1U    /*!< Write flag for cmd52 and cmd53     */

#define SDIO_BUS_SPEED_SDR12   0x00U   /*!< SDIO bus speed mode SDR12          */
#define SDIO_BUS_SPEED_SDR25   0x02U   /*!< SDIO bus speed mode SDR25          */
#define SDIO_BUS_SPEED_SDR50   0x04U   /*!< SDIO bus speed mode SDR50          */
#define SDIO_BUS_SPEED_DDR50   0x08U   /*!< SDIO bus speed mode DDR50          */

#define SDIO_CCCR_REG_NUMBER   0x16U   /*!< SDIO card cccr register number     */

#define SDIO_OCR_VDD_32_33     (1U << 20U)
#define SDIO_OCR_SDIO_S18R     (1U << 24U)
/**
  * @}
  */

/* Private macro -----------------------------------------------------------------------------------------------------*/
#define IS_SDIO_RAW_FLAG(ReadAfterWrite)   (((ReadAfterWrite) == HAL_SDIO_WRITE_ONLY) || \
                                            ((ReadAfterWrite) == HAL_SDIO_READ_AFTER_WRITE))

#define IS_SDIO_FUNCTION(FN)        (((FN) >= HAL_SDIO_FUNCTION_1) && ((FN) <= HAL_SDIO_FUNCTION_7))

#define IS_SDIO_SUPPORTED_BLOCK_SIZE(BLOCKSIZE) (((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_1BYTE)    || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_2BYTE)    || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_4BYTE)    || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_8BYTE)    || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_16BYTE)   || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_32BYTE)   || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_64BYTE)   || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_128BYTE)  || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_256BYTE)  || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_512BYTE)  || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_1024BYTE) || \
                                                 ((BLOCKSIZE) == HAL_SDIO_DATA_BLOCK_SIZE_2048BYTE))

/* Private functions -------------------------------------------------------------------------------------------------*/
/** @defgroup SDIO_Private_Functions SDIO Private Functions
  * @{
  */
static HAL_StatusTypeDef SDIO_InitCard(SDIO_HandleTypeDef *hsdio);
static HAL_StatusTypeDef SDIO_ReadDirect(SDIO_HandleTypeDef *hsdio, uint32_t addr, uint32_t raw, uint32_t function_nbr,
                                         uint8_t *pData);
static HAL_StatusTypeDef SDIO_WriteDirect(SDIO_HandleTypeDef *hsdio, uint32_t addr, uint32_t raw, uint32_t function_nbr,
                                          uint8_t *pData);
static HAL_StatusTypeDef SDIO_WriteExtended(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *cmd_arg,
                                            uint8_t *pData, uint16_t Size_byte);
static uint8_t SDIO_Convert_Block_Size(SDIO_HandleTypeDef *hsdio, uint32_t block_size);
static HAL_StatusTypeDef SDIO_IOFunction_IRQHandler(SDIO_HandleTypeDef *hsdio);
/**
  * @}
  */

/* Exported functions --------------------------------------------------------*/
/** @addtogroup SDIO_Exported_Functions
  * @{
  */
/** @addtogroup SDIO_Exported_Functions_Group1
  *  @brief   Initialization and de-initialization functions
  *
@verbatim
  ==============================================================================
          ##### Initialization and de-initialization functions #####
  ==============================================================================
  [..]
    This section provides functions allowing to initialize/de-initialize the SDIO
    device to be ready for use.

@endverbatim
  * @{
  */
/**
  * @brief  Initializes the SDIO according to the specified parameters in the
            SDIO_HandleTypeDef and create the associated handle.
  * @param  hsdio: Pointer to the SDIO handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_Init(SDIO_HandleTypeDef *hsdio)
{
  SDIO_InitTypeDef Init;
  uint32_t sdmmc_clk;
  uint8_t data;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDMMC_ALL_INSTANCE(hsdio->Instance));
  assert_param(IS_SDMMC_CLOCK_EDGE(hsdio->Init.ClockEdge));
  assert_param(IS_SDMMC_CLOCK_POWER_SAVE(hsdio->Init.ClockPowerSave));
  assert_param(IS_SDMMC_BUS_WIDE(hsdio->Init.BusWide));
  assert_param(IS_SDMMC_HARDWARE_FLOW_CONTROL(hsdio->Init.HardwareFlowControl));
  assert_param(IS_SDMMC_CLKDIV(hsdio->Init.ClockDiv));

  /* Check the SDIO handle allocation */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_RESET)
  {
#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1U)
    /* Reset Callback pointers in HAL_SDIO_STATE_RESET only */
    hsdio->TxCpltCallback = HAL_SDIO_TxCpltCallback;
    hsdio->RxCpltCallback = HAL_SDIO_RxCpltCallback;
    hsdio->ErrorCallback  = HAL_SDIO_ErrorCallback;
#if (USE_SDIO_TRANSCEIVER != 0U)
    if (hsdio->Init.TranceiverPresent == SDMMC_TRANSCEIVER_PRESENT)
    {
      hsdio->DriveTransceiver_1_8V_Callback = HAL_SDIO_DriveTransceiver_1_8V_Callback;
    }
#endif /* USE_SDIO_TRANSCEIVER */

    if (hsdio->MspInitCallback == NULL)
    {
      hsdio->MspInitCallback = HAL_SDIO_MspInit;
    }
    /* Init the low level hardware */
    hsdio->MspInitCallback(hsdio);
#else
    /* Init the low level hardware : GPIO, CLOCK, CORTEX...etc */
    HAL_SDIO_MspInit(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */
  }

  Init.ClockEdge           = SDMMC_CLOCK_EDGE_RISING;
  Init.ClockPowerSave      = SDMMC_CLOCK_POWER_SAVE_DISABLE;
  Init.BusWide             = SDMMC_BUS_WIDE_1B;
  Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;

  sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC);
  if (sdmmc_clk == 0U)
  {
    hsdio->ErrorCode = SDMMC_ERROR_INVALID_PARAMETER;
    return HAL_ERROR;
  }
  Init.ClockDiv = sdmmc_clk / (2U * SDIO_INIT_FREQ);
  /* Initialize SDMMC peripheral interface with default configuration */
  (void)SDMMC_Init(hsdio->Instance, Init);

  /* Set Power State to ON */
  (void)SDMMC_PowerState_ON(hsdio->Instance);

  /* wait 74 Cycles: required power up waiting time before starting the SDIO initialization sequence */
  sdmmc_clk = sdmmc_clk / (2U * Init.ClockDiv);
  HAL_Delay(1U + (74U * 1000U / (sdmmc_clk)));

  if (hsdio->SDIO_IdentifyCard == NULL)
  {
    hsdio->SDIO_IdentifyCard = SDIO_InitCard;
  }
  /* SDIO enumeration sequence */
  if (hsdio->SDIO_IdentifyCard(hsdio) != HAL_OK)
  {
    hsdio->State = HAL_SDIO_STATE_RESET;
    return HAL_ERROR;
  }

  /* Configure the SDMMC user parameters */
  Init.ClockEdge           = hsdio->Init.ClockEdge;
  Init.ClockPowerSave      = hsdio->Init.ClockPowerSave;
  Init.BusWide             = hsdio->Init.BusWide;
  Init.HardwareFlowControl = hsdio->Init.HardwareFlowControl;
  Init.ClockDiv            = hsdio->Init.ClockDiv;
  (void)SDMMC_Init(hsdio->Instance, Init);

  data = (hsdio->Init.BusWide == HAL_SDIO_4_WIRES_MODE) ? 2U : 0U;
  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR4_SD_BYTE3, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &data) != HAL_OK)
  {
    return HAL_ERROR;
  }

  hsdio->Context = SDIO_CONTEXT_NONE;
  hsdio->State = HAL_SDIO_STATE_READY;

  return HAL_OK;
}

/**
  * @brief  De-Initializes the SDIO device.
  * @param  hsdio: Pointer to the SDIO handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_DeInit(SDIO_HandleTypeDef *hsdio)
{
  /* Check the parameters */
  assert_param(IS_SDMMC_ALL_INSTANCE(hsdio->Instance));

  /* Check the SDIO handle allocation */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  /* Set Power State to OFF */
  (void)SDMMC_PowerState_OFF(hsdio->Instance);

#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1U)
  if (hsdio->MspDeInitCallback == NULL)
  {
    hsdio->MspDeInitCallback = HAL_SDIO_MspDeInit;
  }

  /* DeInit the low level hardware */
  hsdio->MspDeInitCallback(hsdio);
#else
  /* De-Initialize the MSP layer */
  HAL_SDIO_MspDeInit(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */

  hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
  hsdio->State = HAL_SDIO_STATE_RESET;

  return HAL_OK;
}

/**
  * @brief  Initializes the SDIO MSP.
  * @param  hsdio: Pointer to SDIO handle
  * @retval None
  */
__weak void HAL_SDIO_MspInit(SDIO_HandleTypeDef *hsdio)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_MspInit could be implemented in the user file
   */
}

/**
  * @brief  De-Initialize SDIO MSP.
  * @param  hsdio: Pointer to SDIO handle
  * @retval None
  */
__weak void HAL_SDIO_MspDeInit(SDIO_HandleTypeDef *hsdio)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_MspDeInit could be implemented in the user file
   */
}
/**
  * @}
  */

/** @addtogroup SDIO_Exported_Functions_Group2
  *  @brief
  *
@verbatim
  ==============================================================================
          ##### Initialization and de-initialization functions #####
  ==============================================================================
  [..]
    This subsection provides a set of functions allowing to re-configure the SDIO peripheral.

@endverbatim
  * @{
  */
/**
  * @brief  Enables wide bus operation for the requested card if supported by card.
  * @param  hsdio: Pointer to SDIO handle
  * @param  BusWide: Specifies the SDIO card wide bus mode
  *          This parameter can be one of the following values:
  *            @arg SDMMC_BUS_WIDE_8B: 8-bit data transfer
  *            @arg SDMMC_BUS_WIDE_4B: 4-bit data transfer
  *            @arg SDMMC_BUS_WIDE_1B: 1-bit data transfer
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_SetDataBusWidth(SDIO_HandleTypeDef *hsdio, uint32_t BusWide)
{
  uint8_t data;
  HAL_StatusTypeDef error_state = HAL_OK;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    data = (BusWide == HAL_SDIO_4_WIRES_MODE) ? 2U : 0U;
    MODIFY_REG(hsdio->Instance->CLKCR, SDMMC_CLKCR_WIDBUS,
               (BusWide == HAL_SDIO_4_WIRES_MODE) ? SDMMC_BUS_WIDE_4B : SDMMC_BUS_WIDE_1B);

    if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR4_SD_BYTE3, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &data) != HAL_OK)
    {
      error_state =  HAL_ERROR;
    }
  }
  else
  {
    error_state =  HAL_ERROR;
  }

  return error_state;
}

/**
  * @brief  Update the SDIO Clock.
  * @param  hsdio: Pointer to SDIO handle.
  * @param  ClockSpeed: SDIO Clock speed.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_ConfigFrequency(SDIO_HandleTypeDef *hsdio, uint32_t ClockSpeed)
{
  uint32_t ClockDiv;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    ClockDiv = (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC)) / (2U * ClockSpeed);
    MODIFY_REG(hsdio->Instance->CLKCR, SDMMC_CLKCR_CLKDIV, ClockDiv);
  }
  else
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Set the SDIO block size.
  * @param  hsdio: Pointer to SDIO handle
  * @param  function_nbr: Specifies the SDIO function number.
  * @param  BlockSize: Specifies the SDIO Block size to set.
  *         This parameter can be one of the following values @ref SDIO_Exported_Constansts_Group7.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_SetBlockSize(SDIO_HandleTypeDef *hsdio, uint8_t function_nbr, uint16_t BlockSize)
{
  HAL_SDIO_ExtendedCmd_TypeDef cmd53;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(function_nbr));
  assert_param(IS_SDIO_SUPPORTED_BLOCK_SIZE(BlockSize));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  /* Set SDIO F1 block size */
  cmd53.IOFunctionNbr = SDIO_FUNCTION_0;
  cmd53.OpCode = HAL_SDIO_OP_CODE_AUTO_INC;
  cmd53.Block_Mode = HAL_SDIO_MODE_BYTE;
  cmd53.Reg_Addr = (function_nbr * 0x100UL) + 0x10UL;
  if (SDIO_WriteExtended(hsdio, &cmd53, (uint8_t *)(&BlockSize), 2U) != HAL_OK)
  {
    return HAL_ERROR;
  }

  hsdio->block_size = BlockSize;

  return HAL_OK;
}

/**
  * @brief  Configure the data rate.
  * @param  hsdio: Pointer to SDIO handle
  * @param  DataRate: Specifies the SDIO data rate to set.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_SetSpeedMode(SDIO_HandleTypeDef *hsdio, uint32_t DataRate)
{
  HAL_StatusTypeDef errorstate = HAL_OK;
  uint8_t data;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  switch (DataRate)
  {
    case HAL_SDIOS_DATA_RATE_SDR25:
      data = SDIO_BUS_SPEED_SDR25;
      errorstate = SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR16_SD_BYTE3, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &data);
      break;

    case HAL_SDIOS_DATA_RATE_SDR50:
      data = SDIO_BUS_SPEED_SDR50;
      errorstate = SDIO_WriteDirect(hsdio, ((SDIO_FUNCTION_0 << 2U) | (SDIO_FUNCTION_0 << 1U) | (SDIO_FUNCTION_0 << 14U)
                                            | SDMMC_SDIO_CCCR16_SD_BYTE3), HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &data);
      MODIFY_REG(hsdio->Instance->CLKCR, SDMMC_CLKCR_BUSSPEED, SDMMC_CLKCR_BUSSPEED);
      break;

    case HAL_SDIOS_DATA_RATE_DDR50:
      data = SDIO_BUS_SPEED_DDR50;
      errorstate = SDIO_WriteDirect(hsdio, ((SDIO_FUNCTION_0 << 2) | (SDIO_FUNCTION_0 << 1) | (SDIO_FUNCTION_0 << 14) |
                                            SDMMC_SDIO_CCCR16_SD_BYTE3), HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &data);
      MODIFY_REG(hsdio->Instance->CLKCR, SDMMC_CLKCR_DDR | SDMMC_CLKCR_BUSSPEED,
                 SDMMC_CLKCR_DDR | SDMMC_CLKCR_BUSSPEED);
      break;
    default: /* SDR12 */
      break;
  }

  return (errorstate != HAL_OK) ? HAL_ERROR : HAL_OK;
}

/**
  * @brief  Reset SDIO Card
  * @param  hsdio: Pointer to SDIO handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_CardReset(SDIO_HandleTypeDef *hsdio)
{
  uint8_t data = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  /**  To reset the SDIO module by CMD52 with writing to RES in CCCR or send CMD0 the card shall change the speed mode
    *  default speed mode.
    *  The reset cmd (cmd0) is only used for memory. In order to reset an I/O card or the I/O portion of a combo card,
    *  Use CMD52 to write 1 to the RES bit in the CCC(bit3 of register 6).
    */
  if (SDIO_WriteDirect(hsdio, ((SDIO_FUNCTION_0 << 2) | (SDIO_FUNCTION_0 << 1) | (SDIO_FUNCTION_0 << 14) |
                               SDMMC_SDIO_CCCR4_SD_BYTE2),
                       HAL_SDIO_WRITE_ONLY,
                       0U,
                       &data) != HAL_OK)
  {
    return HAL_ERROR;
  }

  hsdio->State = HAL_SDIO_STATE_RESET;

  return HAL_OK;
}

/**
  * @brief  Get Card Common Control register (CCCR).
  * @param  hsdio: Pointer to SDIO handle.
  * @param  pCccr: Pointer to Cccr register.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_GetCardCommonControlRegister(SDIO_HandleTypeDef *hsdio, HAL_SDIO_CCCR_TypeDef *pCccr)
{
  uint8_t tempBuffer[256] = {0U};
  uint32_t count;

  assert_param(hsdio != NULL);
  assert_param(pCccr != NULL);

  if ((hsdio == NULL) || (pCccr == NULL))
  {
    return HAL_ERROR;
  }

  for (count = 0U; count <= SDIO_CCCR_REG_NUMBER; count++)
  {
    if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR0 + count, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &tempBuffer[count]) !=
        HAL_OK)
    {
      return HAL_ERROR;
    }
  }

  pCccr->cccr_revision = tempBuffer[0] & 0x0FU;
  pCccr->sdio_revision = (tempBuffer[0] & 0xF0U) >> 4U;
  pCccr->sd_spec_revision = tempBuffer[0x01U] & 0x0FU;
  pCccr->bus_width_8Bit = ((tempBuffer[0x07U] & 0x04U) != 0U) ? HAL_SDIO_BUS_WIDTH_8BIT_SUPPORTED
                          : HAL_SDIO_BUS_WIDTH_8BIT_NOT_SUPPORTED;
  pCccr->card_capability = (tempBuffer[0x08U] & 0xDFUL);
  /* common CIS pointer */
  pCccr->commonCISPointer = tempBuffer[0x09U] | ((uint32_t)tempBuffer[(uint32_t)0x09U + 1U] << 8U) |
                            ((uint32_t)tempBuffer[(uint32_t)0x09U + 2U] << 16U);

  return HAL_OK;
}

/**
  * @brief  Get Card Function Basic register(FBR).
  * @param  hsdio: Pointer to SDIO handle.
  * @param  pFbr: Pointer to Fbr register.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_GetCardFBRRegister(SDIO_HandleTypeDef *hsdio, HAL_SDIO_FBR_t *pFbr)
{
  uint8_t tempBuffer[256] = {0U};
  uint32_t count;
  uint8_t func_idx;

  assert_param(hsdio != NULL);
  assert_param(pFbr != NULL);

  if ((hsdio == NULL) || (pFbr == NULL))
  {
    return HAL_ERROR;
  }

  for (func_idx = 2U; func_idx <= SDIO_MAX_IO_NUMBER; func_idx++)
  {
    for (count = 0U; count <= SDIO_CCCR_REG_NUMBER; count++)
    {
      if (SDIO_ReadDirect(hsdio, (((uint32_t)SDMMC_SDIO_F1BR0 * (uint32_t)func_idx) + count),
                          HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &tempBuffer[count]) != HAL_OK)
      {
        return HAL_ERROR;
      }
    }
    pFbr[(uint32_t)func_idx - 1U].ioStdFunctionCode = tempBuffer[0U] & 0x0FU;
    pFbr[(uint32_t)func_idx - 1U].ioExtFunctionCode = tempBuffer[1U];
    pFbr[(uint32_t)func_idx - 1U].ioPointerToCIS = tempBuffer[9U] | ((uint32_t)tempBuffer[10U] << 8U) |
                                                   ((uint32_t)tempBuffer[11U] << 16U);
    pFbr[(uint32_t)func_idx - 1U].ioPointerToCSA = tempBuffer[12U] | ((uint32_t)tempBuffer[13U] << 8U) |
                                                   ((uint32_t)tempBuffer[14U] << 16U);
    if ((tempBuffer[2U] & 0x01U) != 0U)
    {
      pFbr[(uint32_t)func_idx - 1U].flags |= (uint8_t)HAL_SDIO_FBR_SUPPORT_POWER_SELECTION;
    }
    if ((tempBuffer[0U] & 0x40U) != 0U)
    {
      pFbr[(uint32_t)func_idx - 1U].flags |= (uint8_t)HAL_SDIO_FBR_SUPPORT_CSA;
    }
  }

  return HAL_OK;
}
/**
  * @}
  */

/** @addtogroup SDIO_Exported_Functions_Group3
  *  @brief
  *
@verbatim
  ==============================================================================
          ##### Data management functions #####
  ==============================================================================
  [..]
    This subsection provides a set of functions allowing to manage the data transfer from/to SDIO card.

@endverbatim
  * @{
  */
/**
  * @brief  Read data from a specified address using the direct mode through cmd52.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Specifies the SDIO Argument.
  * @param  pData: pointer to the buffer that will contain the received data.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_ReadDirect(SDIO_HandleTypeDef *hsdio, HAL_SDIO_DirectCmd_TypeDef *Argument, uint8_t *pData)
{
  uint32_t cmd;
  uint32_t errorstate;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(pData != NULL);
  assert_param(IS_SDIO_RAW_FLAG(Argument->ReadAfterWrite));

  if ((hsdio == NULL) || (Argument == NULL) || (NULL == pData))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    cmd = SDIO_READ << 31U;
    cmd |= (((uint32_t)Argument->IOFunctionNbr) << 28U);
    cmd |= (((uint32_t)Argument->ReadAfterWrite) << 27U);
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= 0U;
    errorstate = SDMMC_SDIO_CmdReadWriteDirect(hsdio->Instance, cmd, pData);

    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        /* Clear all the static flags */
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }

    __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);

    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

    hsdio->State = HAL_SDIO_STATE_READY;
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}


/**
  * @brief  Read data from a specified address using the direct mode through cmd52.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Specifies the SDIO Argument.
  * @param  Data: pointer to the buffer that will contain the received data.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_WriteDirect(SDIO_HandleTypeDef *hsdio, HAL_SDIO_DirectCmd_TypeDef *Argument, uint8_t Data)
{
  uint32_t cmd;
  uint32_t errorstate;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(IS_SDIO_RAW_FLAG(Argument->ReadAfterWrite));

  if ((hsdio == NULL) || (Argument == NULL))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    cmd = SDIO_WRITE << 31U;
    cmd |= ((uint32_t)Argument->IOFunctionNbr) << 28U;
    cmd |= ((uint32_t)Argument->ReadAfterWrite) << 27U;
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= Data;
    errorstate = SDMMC_SDIO_CmdReadWriteDirect(hsdio->Instance, cmd, &Data);
    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        /* Clear all the static flags */
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }

    __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);

    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

    hsdio->State = HAL_SDIO_STATE_READY;
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}

/**
  * @brief  Read data from a specified address using extended mode through cmd53.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Pointer to SDIO argument
  * @param  pData:  pointer to the buffer that will contain the data to transmit
  * @param  Size_byte:   size to read.
  * @param  Timeout_Ms:  Specify timeout value
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_ReadExtended(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *Argument,
                                        uint8_t *pData, uint32_t Size_byte, uint32_t Timeout_Ms)
{
  uint32_t cmd;
  SDMMC_DataInitTypeDef config;
  uint32_t errorstate;
  uint32_t tickstart = HAL_GetTick();
  uint32_t regCount;
  uint8_t byteCount;
  uint32_t data;
  uint32_t dataremaining;
  uint8_t *tempbuff = pData;
  uint32_t nbr_of_block;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(pData != NULL);

  if ((hsdio == NULL) || (Argument == NULL) || (pData == NULL))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    /* Compute how many blocks are to be send for pData of length data_size to be send */
    nbr_of_block = (Size_byte & ~(hsdio->block_size & 1U)) >> __CLZ(__RBIT(hsdio->block_size));

    /* Initialize data control register */
    if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
    {
      hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
    }
    else
    {
      hsdio->Instance->DCTRL = 0U;
    }

    /* Configure the SDIO DPSM (Data Path State Machine) */
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    /* (HAL_SDIO_MODE_BLOCK << 27) corresponds to the block mode bit of the CMD argument */
    if (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK)
    {
      /* (Argument & 0x1FFU) is to get the 9 bits of Block/Byte counts */
      config.DataLength    = (uint32_t)(nbr_of_block * hsdio->block_size);
      config.DataBlockSize = SDIO_Convert_Block_Size(hsdio, hsdio->block_size);
    }
    else
    {
      /* (Argument & 0x1FFU) is to get the 9 bits of Block/Byte counts */
      config.DataLength    = (Size_byte > 0U) ? Size_byte : HAL_SDIO_DATA_BLOCK_SIZE_512BYTE;
      config.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
    }

    config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC ;
    /* (HAL_SDIO_MODE_BLOCK << 27) corresponds to the block mode bit of the CMD argument */
    config.TransferMode = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDMMC_TRANSFER_MODE_BLOCK :
                          SDMMC_TRANSFER_MODE_SDIO;
    config.DPSM         = SDMMC_DPSM_DISABLE;
    (void)SDMMC_ConfigData(hsdio->Instance, &config);
    __SDMMC_CMDTRANS_ENABLE(hsdio->Instance);

    /* Correspond to the write or read bit of the CMD argument */
    /* Read */
    hsdio->Context = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDIO_CONTEXT_READ_MULTIPLE_BLOCK :
                     SDIO_CONTEXT_READ_SINGLE_BLOCK;
    cmd = SDIO_READ << 31U;
    cmd |= Argument->IOFunctionNbr << 28U;
    cmd |= Argument->Block_Mode << 27U;
    cmd |= Argument->OpCode << 26U;
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= (Size_byte & 0x1FFU);
    errorstate = SDMMC_SDIO_CmdReadWriteExtended(hsdio->Instance, cmd);
    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        MODIFY_REG(hsdio->Instance->DCTRL, SDMMC_DCTRL_FIFORST, SDMMC_DCTRL_FIFORST);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }
    /* (SDIO_WRITE << 31) correspond to the write or read bit of the CMD argument */
    /* Poll on SDMMC flags */
    dataremaining = config.DataLength;

    while (!__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL |
                                SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND))
    {
      if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_RXFIFOHF) && (dataremaining >= 32U))
      {
        /* Read data from SDMMC Rx FIFO */
        for (regCount = 0U; regCount < 8U; regCount++)
        {
          data = SDMMC_ReadFIFO(hsdio->Instance);
          *tempbuff = (uint8_t)(data & 0xFFU);
          tempbuff++;
          *tempbuff = (uint8_t)((data >> 8U) & 0xFFU);
          tempbuff++;
          *tempbuff = (uint8_t)((data >> 16U) & 0xFFU);
          tempbuff++;
          *tempbuff = (uint8_t)((data >> 24U) & 0xFFU);
          tempbuff++;
        }
        dataremaining -= 32U;
      }
      else if (dataremaining < 32U)
      {
        while ((dataremaining > 0U) && !(__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_RXFIFOE)))
        {
          data = SDMMC_ReadFIFO(hsdio->Instance);
          for (byteCount = 0U; byteCount < 4U; byteCount++)
          {
            if (dataremaining > 0U)
            {
              *tempbuff = (uint8_t)((data >> (byteCount * 8U)) & 0xFFU);
              tempbuff++;
              dataremaining--;
            }
          }
        }
      }
      else
      {
        /* Nothing to do */
      }
      if ((HAL_GetTick() - tickstart) >=  Timeout_Ms)
      {
        /* Clear all the static flags */
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        hsdio->ErrorCode |= HAL_SDIO_ERROR_TIMEOUT;
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_TIMEOUT;
      }
    }
    __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);
    /* Get error state */
    if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DTIMEOUT))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_TIMEOUT;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DCRCFAIL))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_CRC_FAIL;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_RXOVERR))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      /* (SDIO_WRITE << 31) correspond to the write or read bit of the CMD argument */
      hsdio->ErrorCode |= HAL_SDIO_ERROR_RX_OVERRUN;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (hsdio->ErrorCode == SDMMC_ERROR_INVALID_PARAMETER)
    {
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else
    {
      /* Nothing to do */
    }

    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

    hsdio->State = HAL_SDIO_STATE_READY;
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}

/**
  * @brief  Write data from a specified address using extended mode through cmd53.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Pointer to SDIO argument
  * @param  pData:  pointer to the buffer that will contain the data to transmit
  * @param  Size_byte:  Block size to write.
  * @param  Timeout_Ms:  Specify timeout value
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_WriteExtended(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *Argument,
                                         uint8_t *pData, uint32_t Size_byte, uint32_t Timeout_Ms)
{
  uint32_t cmd;
  SDMMC_DataInitTypeDef config;
  uint32_t errorstate;
  uint32_t tickstart = HAL_GetTick();
  uint32_t regCount;
  uint8_t byteCount;
  uint32_t data;
  uint32_t dataremaining;
  uint8_t *u32tempbuff = pData;
  uint32_t nbr_of_block;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(pData != NULL);

  if ((hsdio == NULL) || (Argument == NULL) || (pData == NULL))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    /* Compute how many blocks are to be send for pData of length data_size to be send */
    nbr_of_block = (Size_byte & ~(hsdio->block_size & 1U)) >> __CLZ(__RBIT(hsdio->block_size));

    /* Initialize data control register */
    if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
    {
      hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
    }
    else
    {
      hsdio->Instance->DCTRL = 0U;
    }

    /* Configure the SDIO DPSM (Data Path State Machine) */
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    if (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK)
    {
      config.DataLength    = (uint32_t)(nbr_of_block * hsdio->block_size);
      config.DataBlockSize = SDIO_Convert_Block_Size(hsdio, hsdio->block_size);
    }
    else
    {
      config.DataLength    = (Size_byte > 0U) ? Size_byte : HAL_SDIO_DATA_BLOCK_SIZE_512BYTE;
      config.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
    }

    config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
    /* (HAL_SDIO_MODE_BLOCK << 27) corresponds to the block mode bit of the CMD argument */
    config.TransferMode  = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDMMC_TRANSFER_MODE_BLOCK :
                           SDMMC_TRANSFER_MODE_SDIO;
    config.DPSM          = SDMMC_DPSM_DISABLE;
    (void)SDMMC_ConfigData(hsdio->Instance, &config);
    __SDMMC_CMDTRANS_ENABLE(hsdio->Instance);

    /* Correspond to the write or read bit of the CMD argument */
    hsdio->Context = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDIO_CONTEXT_WRITE_MULTIPLE_BLOCK :
                     SDIO_CONTEXT_WRITE_SINGLE_BLOCK;
    cmd = SDIO_WRITE << 31U;
    cmd |= Argument->IOFunctionNbr << 28U;
    cmd |= Argument->Block_Mode << 27U;
    cmd |= Argument->OpCode << 26U;
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= (Size_byte & 0x1FFU);
    errorstate = SDMMC_SDIO_CmdReadWriteExtended(hsdio->Instance, cmd);
    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        MODIFY_REG(hsdio->Instance->DCTRL, SDMMC_DCTRL_FIFORST, SDMMC_DCTRL_FIFORST);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }
    /* Write block(s) in polling mode */
    dataremaining = config.DataLength;
    while (!__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXUNDERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT |
                                SDMMC_FLAG_DATAEND))
    {

      if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXFIFOHE) && (dataremaining >= 32U))
      {
        /* Read data from SDMMC Rx FIFO */
        for (regCount = 0U; regCount < 8U; regCount++)
        {
          hsdio->Instance->FIFO = *u32tempbuff;
          u32tempbuff++;
        }
        dataremaining -= 32U;
      }
      else if ((dataremaining < 32U) && (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXFIFOHE | SDMMC_FLAG_TXFIFOE)))
      {
        uint8_t *u8buff = (uint8_t *)u32tempbuff;
        while (dataremaining > 0U)
        {
          data = 0U;
          for (byteCount = 0U; (byteCount < 4U) && (dataremaining > 0U); byteCount++)
          {
            data |= ((uint32_t)(*u8buff) << (byteCount << 3U));
            u8buff++;
            dataremaining--;
          }
          hsdio->Instance->FIFO = data;
        }
      }
      if (((HAL_GetTick() - tickstart) >=  Timeout_Ms))
      {
        /* Clear all the static flags */
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        hsdio->ErrorCode |= HAL_SDIO_ERROR_TIMEOUT;
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_TIMEOUT;
      }
    }

    __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);
    /* Get error state */
    if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DTIMEOUT))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_TIMEOUT;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DCRCFAIL))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_CRC_FAIL;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXUNDERR))
    {
      /* Clear all the static flags */
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      /* (SDIO_WRITE << 31) correspond to the write or read bit of the CMD argument */
      hsdio->ErrorCode |= HAL_SDIO_ERROR_TX_UNDERRUN;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else if (hsdio->ErrorCode == SDMMC_ERROR_INVALID_PARAMETER)
    {
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_ERROR;
    }
    else
    {
      /* Nothing to do */
    }

    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

    hsdio->State = HAL_SDIO_STATE_READY;
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}

/**
  * @brief  Read data from a specified address using extended mode through cmd53 in DMA mode.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Pointer to SDIO argument
  * @param  pData:  pointer to the buffer that will contain the data to transmit
  * @param  Size_byte:  Block size to write.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_ReadExtended_DMA(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *Argument,
                                            uint8_t *pData, uint32_t Size_byte)
{
  SDMMC_DataInitTypeDef config;
  uint32_t errorstate;
  uint8_t *p_dma_buffer;
  uint32_t cmd;
  uint32_t nbr_of_block;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(pData != NULL);

  if ((hsdio == NULL) || (Argument == NULL) || (pData == NULL))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    /* Initialize data control register */
    if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
    {
      hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
    }
    else
    {
      hsdio->Instance->DCTRL = 0U;
    }

    p_dma_buffer = (uint8_t *)pData;
    hsdio->pRxBuffPtr = (uint8_t *)pData;
    hsdio->RxXferSize = Size_byte;
    hsdio->next_data_addr = (uint32_t)pData;

    /* Compute how many blocks are to be send for pData of length data_size to be send */
    nbr_of_block = (Size_byte & ~(hsdio->block_size & 1U)) >> __CLZ(__RBIT(hsdio->block_size));

    if (nbr_of_block != 0U)
    {
      hsdio->remaining_data = (Size_byte - (hsdio->block_size * nbr_of_block));
      hsdio->next_reg_addr = (Argument->Reg_Addr) | ((((nbr_of_block * hsdio->block_size) >> 1U) & 0x3FFFU) << 1U)
                             | ((hsdio->remaining_data <= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? 1U : 0U);
      hsdio->next_data_addr += (nbr_of_block * hsdio->block_size);
    }
    else
    {
      hsdio->next_data_addr += (Size_byte < HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? Size_byte :
                               HAL_SDIO_DATA_BLOCK_SIZE_512BYTE;
      if (hsdio->remaining_data != 0U)
      {
        hsdio->remaining_data = (Size_byte >= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ?
                                (Size_byte - HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) :
                                (Size_byte - hsdio->remaining_data);
        hsdio->next_reg_addr += (Size_byte >= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? \
                                (HAL_SDIO_DATA_BLOCK_SIZE_512BYTE + 1U) : (Size_byte + 1U);
      }
    }

    /* DMA configuration (use single buffer) */
    hsdio->Instance->IDMACTRL  = SDMMC_ENABLE_IDMA_SINGLE_BUFF;
    hsdio->Instance->IDMABASE0 = (uint32_t)p_dma_buffer;

    /* Configure the SD DPSM (Data Path State Machine) */
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    if (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK)
    {
      config.DataLength    = (uint32_t)(nbr_of_block * hsdio->block_size);
      config.DataBlockSize = SDIO_Convert_Block_Size(hsdio, hsdio->block_size);
    }
    else
    {
      config.DataLength    = (Size_byte > 0U) ? Size_byte : HAL_SDIO_DATA_BLOCK_SIZE_512BYTE;
      config.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
    }

    config.TransferDir   = SDMMC_TRANSFER_DIR_TO_SDMMC ;
    config.TransferMode  = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDMMC_TRANSFER_MODE_BLOCK :
                           SDMMC_TRANSFER_MODE_SDIO;
    config.DPSM          = SDMMC_DPSM_DISABLE;
    (void)SDMMC_ConfigData(hsdio->Instance, &config);

    __SDMMC_CMDTRANS_ENABLE(hsdio->Instance);

    /* Read */
    hsdio->Context = (uint32_t)((Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDIO_CONTEXT_READ_MULTIPLE_BLOCK :
                                SDIO_CONTEXT_READ_SINGLE_BLOCK) | SDIO_CONTEXT_DMA;

    cmd = SDIO_READ << 31U;
    cmd |= Argument->IOFunctionNbr << 28U;
    cmd |= Argument->Block_Mode << 27U;
    cmd |= Argument->OpCode << 26U;
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= ((nbr_of_block == 0U) ? Size_byte :  nbr_of_block) & 0x1FFU;
    errorstate = SDMMC_SDIO_CmdReadWriteExtended(hsdio->Instance, cmd);
    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        MODIFY_REG(hsdio->Instance->DCTRL, SDMMC_DCTRL_FIFORST, SDMMC_DCTRL_FIFORST);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }
    /* Enable interrupt */
    __HAL_SDIO_ENABLE_IT(hsdio, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND));
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}

/**
  * @brief  Write data from a specified address using extended mode through cmd53 in DMA mode.
  * @param  hsdio: Pointer to SDIO handle
  * @param  Argument: Pointer to SDIO argument
  * @param  pData:  pointer to the buffer that will contain the data to transmit
  * @param  Size_byte:  Block size to write.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_WriteExtended_DMA(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *Argument,
                                             uint8_t *pData, uint32_t Size_byte)
{
  uint32_t cmd;
  SDMMC_DataInitTypeDef config;
  uint32_t errorstate;
  uint8_t *p_dma_buffer;
  uint32_t nbr_of_block;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(Argument != NULL);
  assert_param(pData != NULL);

  if ((hsdio == NULL) || (Argument == NULL) || (pData == NULL))
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;
    hsdio->State = HAL_SDIO_STATE_BUSY;

    /* Initialize data control register */
    if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
    {
      hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
    }
    else
    {
      hsdio->Instance->DCTRL = 0U;
    }

    p_dma_buffer = (uint8_t *)pData;
    hsdio->pTxBuffPtr = (uint8_t *)pData;
    hsdio->TxXferSize = Size_byte;
    hsdio->next_data_addr = (uint32_t)pData;

    nbr_of_block = (Size_byte & ~(hsdio->block_size & 1U)) >> __CLZ(__RBIT(hsdio->block_size));

    if (nbr_of_block != 0U)
    {
      hsdio->remaining_data = (Size_byte - (hsdio->block_size * nbr_of_block));
      if (hsdio->block_size <= 128U)
      {
        hsdio->next_reg_addr = (Argument->Reg_Addr) |
                               ((((nbr_of_block * hsdio->block_size) >> 1U) & 0x3FFFU) << 1U) |
                               ((hsdio->remaining_data <= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? 1U : 0U);
      }
      else
      {
        hsdio->next_reg_addr = (nbr_of_block * hsdio->block_size) >> 1U;
      }
      hsdio->next_data_addr += (nbr_of_block * hsdio->block_size);
    }
    else
    {
      hsdio->remaining_data = (Size_byte >= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ?
                              (Size_byte - HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) :
                              (Size_byte - hsdio->remaining_data);
      if (hsdio->remaining_data != 0U)
      {
        hsdio->remaining_data = (Size_byte >= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ?
                                (Size_byte - HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) :
                                (Size_byte - hsdio->remaining_data);
        hsdio->next_reg_addr += ((Size_byte >= HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? \
                                 (HAL_SDIO_DATA_BLOCK_SIZE_512BYTE >> 1U) : (Size_byte >> 1U)) |
                                (((hsdio->remaining_data > 0U) ? 0U : 1U));
      }
      hsdio->next_data_addr += (Size_byte < HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? Size_byte :
                               HAL_SDIO_DATA_BLOCK_SIZE_512BYTE;
    }

    /* DMA configuration (use single buffer) */
    hsdio->Instance->IDMACTRL  = SDMMC_ENABLE_IDMA_SINGLE_BUFF;
    hsdio->Instance->IDMABASE0 = (uint32_t)p_dma_buffer;

    /* Configure the SDIO DPSM (Data Path State Machine) */
    config.DataTimeOut   = SDMMC_DATATIMEOUT;
    if (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK)
    {
      config.DataLength    = (uint32_t)(nbr_of_block * hsdio->block_size);
      config.DataBlockSize = SDIO_Convert_Block_Size(hsdio, hsdio->block_size);
    }
    else
    {
      config.DataLength = (Size_byte > HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ? HAL_SDIO_DATA_BLOCK_SIZE_512BYTE : Size_byte;
      config.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
    }

    config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
    config.TransferMode  = (Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDMMC_TRANSFER_MODE_BLOCK
                           : SDMMC_TRANSFER_MODE_SDIO;
    config.DPSM          = SDMMC_DPSM_DISABLE;
    (void)SDMMC_ConfigData(hsdio->Instance, &config);

    __SDMMC_CMDTRANS_ENABLE(hsdio->Instance);

    /* Write */
    hsdio->Context = (uint32_t)((Argument->Block_Mode == HAL_SDIO_MODE_BLOCK) ?
                                SDIO_CONTEXT_WRITE_MULTIPLE_BLOCK :
                                SDIO_CONTEXT_WRITE_SINGLE_BLOCK) | SDIO_CONTEXT_DMA;
    cmd = SDIO_WRITE << 31U;
    cmd |= Argument->IOFunctionNbr << 28U;
    cmd |= Argument->Block_Mode << 27U;
    cmd |= Argument->OpCode << 26U;
    cmd |= (Argument->Reg_Addr & 0x1FFFFU) << 9U;
    cmd |= ((nbr_of_block == 0U) ? ((Size_byte > HAL_SDIO_DATA_BLOCK_SIZE_512BYTE) ?
                                    HAL_SDIO_DATA_BLOCK_SIZE_512BYTE : Size_byte) : nbr_of_block) & 0x1FFU;
    errorstate = SDMMC_SDIO_CmdReadWriteExtended(hsdio->Instance, cmd);
    if (errorstate != HAL_SDIO_ERROR_NONE)
    {
      hsdio->ErrorCode |= errorstate;
      if (errorstate != (SDMMC_ERROR_ADDR_OUT_OF_RANGE | SDMMC_ERROR_ILLEGAL_CMD | SDMMC_ERROR_COM_CRC_FAILED |
                         SDMMC_ERROR_GENERAL_UNKNOWN_ERR))
      {
        MODIFY_REG(hsdio->Instance->DCTRL, SDMMC_DCTRL_FIFORST, SDMMC_DCTRL_FIFORST);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
        __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
        hsdio->State = HAL_SDIO_STATE_READY;
        hsdio->Context = SDIO_CONTEXT_NONE;
        return HAL_ERROR;
      }
    }
    /* Enable interrupt */
    __HAL_SDIO_ENABLE_IT(hsdio, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND));
  }
  else
  {
    return HAL_BUSY;
  }

  return HAL_OK;
}

/**
  * @}
  */

/** @addtogroup SDIO_Exported_Functions_Group4
  *  @brief
  *
@verbatim
  ==============================================================================
          ##### IO operation functions #####
  ==============================================================================
  [..]
    This subsection provides a set callback functions allowing to manage the data transfer from/to SDIO card.

@endverbatim
  * @{
  */
/**
  * @brief  This function handles SDIO device interrupt request.
  * @param  hsdio: Pointer to SDIO handle
  * @retval None
  */
void HAL_SDIO_IRQHandler(SDIO_HandleTypeDef *hsdio)
{
  HAL_SDIO_ExtendedCmd_TypeDef CMD53_desc;
  HAL_StatusTypeDef errorstate;
  uint32_t ctx = hsdio->Context;
  uint32_t flags;

  flags   = READ_REG(((SDMMC_TypeDef *)((uint32_t)(hsdio)->Instance))->STA);

  if (READ_BIT(flags, SDMMC_FLAG_SDIOIT) != 0U)
  {
    (void)SDIO_IOFunction_IRQHandler(hsdio);
  }

  if (READ_BIT(flags, SDMMC_FLAG_DATAEND) != 0U)
  {
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_FLAG_DATAEND);

    hsdio->State = HAL_SDIO_STATE_READY;

    __HAL_SDIO_DISABLE_IT(hsdio, SDMMC_IT_DATAEND  | SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR |
                          SDMMC_IT_RXOVERR  | SDMMC_IT_TXFIFOHE | SDMMC_IT_RXFIFOHF);

    __HAL_SDIO_DISABLE_IT(hsdio, SDMMC_IT_IDMABTC);
    __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);

    if ((ctx & SDIO_CONTEXT_DMA) != 0U)
    {
      hsdio->Instance->DLEN = 0;
      hsdio->Instance->IDMACTRL = SDMMC_DISABLE_IDMA;
      if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
      {
        hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
      }
      else
      {
        hsdio->Instance->DCTRL = 0U;
      }

      hsdio->Context = SDIO_CONTEXT_NONE;
      hsdio->State = HAL_SDIO_STATE_READY;
    }

    if (hsdio->remaining_data != 0U)
    {
      CMD53_desc.Block_Mode = HAL_SDIO_MODE_BYTE;
      CMD53_desc.Reg_Addr = hsdio->next_reg_addr;
      CMD53_desc.IOFunctionNbr = 1;
      CMD53_desc.OpCode = 1;
      if (((ctx & SDIO_CONTEXT_READ_SINGLE_BLOCK) != 0U) || ((ctx & SDIO_CONTEXT_READ_MULTIPLE_BLOCK) != 0U))
      {
        hsdio->pRxBuffPtr = (uint8_t *)hsdio->next_data_addr;
        errorstate =  HAL_SDIO_ReadExtended_DMA(hsdio, &CMD53_desc, hsdio->pRxBuffPtr, hsdio->remaining_data);
      }
      else
      {
        hsdio->pTxBuffPtr = (uint8_t *)hsdio->next_data_addr;
        errorstate =  HAL_SDIO_WriteExtended_DMA(hsdio, &CMD53_desc, hsdio->pTxBuffPtr, hsdio->remaining_data);
      }
      if (errorstate != HAL_OK)
      {
#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1)
        hsdio->ErrorCallback(hsdio);
#else
        HAL_SDIO_ErrorCallback(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */
      }
    }
    else if (((ctx & SDIO_CONTEXT_READ_SINGLE_BLOCK) != 0U) || ((ctx & SDIO_CONTEXT_READ_MULTIPLE_BLOCK) != 0U))
    {
#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1U)
      hsdio->RxCpltCallback(hsdio);
#else
      HAL_SDIO_RxCpltCallback(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */
    }
    else
    {
#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1U)
      hsdio->TxCpltCallback(hsdio);
#else
      HAL_SDIO_TxCpltCallback(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */
    }
  }

  if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_RXOVERR | SDMMC_FLAG_TXUNDERR))
  {
#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1)
    hsdio->ErrorCallback(hsdio);
#else
    HAL_SDIO_ErrorCallback(hsdio);
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */
  }
}

/**
  * @brief Tx Transfer completed callbacks
  * @param hsdio: Pointer to SDIO handle
  * @retval None
  */
__weak void HAL_SDIO_TxCpltCallback(SDIO_HandleTypeDef *hsdio)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_TxCpltCallback can be implemented in the user file
   */
}

/**
  * @brief Rx Transfer completed callbacks
  * @param hsdio: Pointer SDIO handle
  * @retval None
  */
__weak void HAL_SDIO_RxCpltCallback(SDIO_HandleTypeDef *hsdio)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_RxCpltCallback can be implemented in the user file
   */
}

/**
  * @brief SDIO error callbacks
  * @param hsdio: Pointer SDIO handle
  * @retval None
  */
__weak void HAL_SDIO_ErrorCallback(SDIO_HandleTypeDef *hsdio)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_ErrorCallback can be implemented in the user file
   */
}

/**
  * @brief SDIO IO Function complete callback
  * @param hsdio: Pointer SDIO handle
  * @param func: SDIO IO Function
  * @retval None
  */
__weak void HAL_SDIO_IOFunctionCallback(SDIO_HandleTypeDef *hsdio, uint32_t func)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);
  UNUSED(func);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_ErrorCallback can be implemented in the user file
   */
}

#if (USE_SDIO_TRANSCEIVER != 0U)
/**
  * @brief  Enable/Disable the SDIO Transceiver 1.8V Mode Callback.
  * @param  hsdio: Pointer SDIO handle
  * @param  status: Voltage Switch State
  * @retval None
  */
__weak  void HAL_SDIO_DriveTransceiver_1_8V_Callback(SDIO_HandleTypeDef *hsdio, FlagStatus status)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsdio);
  UNUSED(status);
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SDIO_EnableTransceiver could be implemented in the user file
   */
}
#endif /* USE_SDIO_TRANSCEIVER  */

#if defined (USE_HAL_SDIO_REGISTER_CALLBACKS) && (USE_HAL_SDIO_REGISTER_CALLBACKS == 1U)
/**
  * @brief  Register a User SDIO Callback
  *         To be used instead of the weak (overridden) predefined callback
  * @param hsdio : SDIO handle
  * @param CallbackID : ID of the callback to be registered
  *        This parameter can be one of the following values:
  *          @arg @ref HAL_SDIO_TX_CPLT_CB_ID                 SDIO Tx Complete Callback ID
  *          @arg @ref HAL_SDIO_RX_CPLT_CB_ID                 SDIO Rx Complete Callback ID
  *          @arg @ref HAL_SDIO_ERROR_CB_ID                   SDIO Error Callback ID
  *          @arg @ref HAL_SDIO_MSP_INIT_CB_ID                SDIO MspInit Callback ID
  *          @arg @ref HAL_SDIO_MSP_DEINIT_CB_ID              SDIO MspDeInit Callback ID
  * @param pCallback : pointer to the Callback function
  * @retval status
  */
HAL_StatusTypeDef HAL_SDIO_RegisterCallback(SDIO_HandleTypeDef *hsdio, HAL_SDIO_CallbackIDTypeDef CallbackID,
                                            pSDIO_CallbackTypeDef pCallback)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(pCallback != NULL);

  if (pCallback == NULL)
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    switch (CallbackID)
    {
      case HAL_SDIO_TX_CPLT_CB_ID :
        hsdio->TxCpltCallback = pCallback;
        break;
      case HAL_SDIO_RX_CPLT_CB_ID :
        hsdio->RxCpltCallback = pCallback;
        break;
      case HAL_SDIO_ERROR_CB_ID :
        hsdio->ErrorCallback = pCallback;
        break;
      case HAL_SDIO_MSP_INIT_CB_ID :
        hsdio->MspInitCallback = pCallback;
        break;
      case HAL_SDIO_MSP_DEINIT_CB_ID :
        hsdio->MspDeInitCallback = pCallback;
        break;
      default :
        /* Update the error code */
        hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else if (hsdio->State == HAL_SDIO_STATE_RESET)
  {
    switch (CallbackID)
    {
      case HAL_SDIO_MSP_INIT_CB_ID :
        hsdio->MspInitCallback = pCallback;
        break;
      case HAL_SDIO_MSP_DEINIT_CB_ID :
        hsdio->MspDeInitCallback = pCallback;
        break;
      default :
        /* Update the error code */
        hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    /* update return status */
    status =  HAL_ERROR;
  }

  return status;
}

/**
  * @brief  Unregister a User SDIO Callback
  *         SDIO Callback is redirected to the weak (overridden) predefined callback.
  * @note   The HAL_SDIO_UnRegisterCallback() may be called before HAL_SDIO_Init() in
  *         HAL_SDIO_STATE_RESET to register callbacks for HAL_SDIO_MSP_INIT_CB_ID
  *         and HAL_SDIO_MSP_DEINIT_CB_ID.
  * @param hsdio : SDIO handle
  * @param CallbackID : ID of the callback to be unregistered
  *        This parameter can be one of the following values @ref SDIO_Exported_Types_Group3.
  * @retval status
  */
HAL_StatusTypeDef HAL_SDIO_UnRegisterCallback(SDIO_HandleTypeDef *hsdio, HAL_SDIO_CallbackIDTypeDef CallbackID)
{
  HAL_StatusTypeDef status = HAL_OK;

  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    switch (CallbackID)
    {
      case HAL_SDIO_TX_CPLT_CB_ID :
        hsdio->TxCpltCallback = HAL_SDIO_TxCpltCallback;
        break;
      case HAL_SDIO_RX_CPLT_CB_ID :
        hsdio->RxCpltCallback = HAL_SDIO_RxCpltCallback;
        break;
      case HAL_SDIO_ERROR_CB_ID :
        hsdio->ErrorCallback = HAL_SDIO_ErrorCallback;
        break;
      case HAL_SDIO_MSP_INIT_CB_ID :
        hsdio->MspInitCallback = HAL_SDIO_MspInit;
        break;
      case HAL_SDIO_MSP_DEINIT_CB_ID :
        hsdio->MspDeInitCallback = HAL_SDIO_MspDeInit;
        break;
      default :
        hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
        status =  HAL_ERROR;
        break;
    }
  }
  else if (hsdio->State == HAL_SDIO_STATE_RESET)
  {
    switch (CallbackID)
    {
      case HAL_SDIO_MSP_INIT_CB_ID :
        hsdio->MspInitCallback = HAL_SDIO_MspInit;
        break;
      case HAL_SDIO_MSP_DEINIT_CB_ID :
        hsdio->MspDeInitCallback = HAL_SDIO_MspDeInit;
        break;
      default :
        hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    status =  HAL_ERROR;
  }

  return status;
}
#endif /* USE_HAL_SDIO_REGISTER_CALLBACKS */

#if (USE_SDIO_TRANSCEIVER != 0U)
/**
  * @brief  Register a User SDIO Transceiver Callback
  *         To be used instead of the weak (overridden) predefined callback
  * @param hsdio : SDIO handle
  * @param pCallback : pointer to the Callback function
  * @retval status
  */
HAL_StatusTypeDef HAL_SDIO_RegisterTransceiverCallback(SDIO_HandleTypeDef *hsdio,
                                                       pSDIO_TransceiverCallbackTypeDef pCallback)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (pCallback == NULL)
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    return HAL_ERROR;
  }

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->DriveTransceiver_1_8V_Callback = pCallback;
  }
  else
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    /* update return status */
    status =  HAL_ERROR;
  }

  return status;
}

/**
  * @brief  Unregister a User SDIO Transceiver Callback
  *         SDIO Callback is redirected to the weak (overridden) predefined callback
  * @param hsdio : SDIO handle
  * @retval status
  */
HAL_StatusTypeDef HAL_SDIO_UnRegisterTransceiverCallback(SDIO_HandleTypeDef *hsdio)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (hsdio->State == HAL_SDIO_STATE_READY)
  {
    hsdio->DriveTransceiver_1_8V_Callback = HAL_SDIO_DriveTransceiver_1_8V_Callback;
  }
  else
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    /* update return status */
    status =  HAL_ERROR;
  }

  return status;
}
#endif /* USE_SDIO_TRANSCEIVER */

/**
  * @brief Register a User SDIO Identification Callback
  * @param hsdio: Pointer to SDIO handle
  * @param pCallback: pointer to the Callback function
  * @retval status
  */
HAL_StatusTypeDef HAL_SDIO_RegisterIdentifyCardCallback(SDIO_HandleTypeDef *hsdio,
                                                        pSDIO_IdentifyCardCallbackTypeDef pCallback)
{
  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(pCallback != NULL);

  if (pCallback == NULL)
  {
    /* Update the error code */
    hsdio->ErrorCode |= HAL_SDIO_ERROR_INVALID_CALLBACK;
    return HAL_ERROR;
  }

  hsdio->SDIO_IdentifyCard = pCallback;

  return HAL_OK;
}
/**
  * @}
  */

/** @addtogroup SDIO_Exported_Functions_Group5
  *  @brief
  *
@verbatim
  ==============================================================================
          ##### Peripheral State and Errors functions #####
  ==============================================================================
  [..]
    This subsection provides a set of functions allowing to control the SDIO card operations.

@endverbatim
  * @{
  */
/**
  * @brief return the SDIO state
  * @param hsdio: Pointer to SDIO handle
  * @retval HAL state
  */
HAL_SDIO_StateTypeDef HAL_SDIO_GetState(const SDIO_HandleTypeDef *hsdio)
{
  return hsdio->State;
}

/**
  * @brief  Return the SDIO error code
  * @param  hsdio : Pointer to a SDIO_HandleTypeDef structure that contains the configuration information.
  * @retval SDIO Error Code
  */
uint32_t HAL_SDIO_GetError(const SDIO_HandleTypeDef *hsdio)
{
  return hsdio->ErrorCode;
}

/**
  * @}
  */

/** @addtogroup SDIO_Exported_Functions_Group6
  *  @brief
  *
@verbatim
  ==============================================================================
          ##### Peripheral IO interrupt #####
  ==============================================================================
  [..]
    This subsection provides a set functions allowing to enable/disable IO functions interrupt features
    on the SDIO card.

@endverbatim
  * @{
  */
/**
  * @brief  Enable SDIO IO interrupt.
  * @param  hsdio: Pointer to SDIO handle
  * @param  IOFunction: Specifies the SDIO IO function.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_EnableIOFunctionInterrupt(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  uint8_t intEn  = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR4, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &intEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already enable , do not need enable again */
  if ((((intEn >> (uint32_t)IOFunction) & 0x01U) == 0x01U) && ((intEn & 0x01U) != 0U))
  {
    return HAL_OK;
  }
  else
  {
    intEn |= (1U << (uint32_t)IOFunction) | 0x01U;
    hsdio->IOInterruptNbr++;
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR4, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0,
                       &intEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  __HAL_SDIO_ENABLE_IT(hsdio, SDMMC_IT_SDIOIT);

  /* Enable host SDIO interrupt operations */
  __SDMMC_OPERATION_ENABLE(hsdio->Instance);

  return HAL_OK;
}

/**
  * @brief  Enable SDIO IO interrupt.
  * @param  hsdio: Pointer to SDIO handle
  * @param  IOFunction: Specifies the SDIO IO function.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_DisableIOFunctionInterrupt(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  uint8_t intEn  = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR4, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &intEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already disable , do not need enable again */
  if (((intEn >> (uint32_t)IOFunction) & 0x01U) == 0x00U)
  {
    return HAL_OK;
  }
  else
  {
    /* disable the interrupt, don't disable the interrupt master here */
    intEn &= ~(1U << (uint32_t)IOFunction);
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR4, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0,
                       &intEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  if (hsdio->IOInterruptNbr > 1U)
  {
    hsdio->IOInterruptNbr--;
  }
  else
  {
    hsdio->IOInterruptNbr = 0U;
    __HAL_SDIO_DISABLE_IT(hsdio, SDMMC_IT_SDIOIT);
  }
  return HAL_OK;
}

/**
  * @brief  Enable SDIO IO Enable.
  * @param  hsdio: Pointer to SDIO handle
  * @param  IOFunction: Specifies the SDIO IO function.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_EnableIOFunction(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  uint8_t ioEn  = 0U;
  uint8_t ioReady = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR0_SD_BYTE2, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &ioEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already enable , do not need to enable again */
  if (((ioEn >> (uint32_t)IOFunction) & 0x01U) == 0x01U)
  {
    return HAL_OK;
  }
  else
  {
    ioEn |= (1U << (uint32_t)IOFunction);
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR0_SD_BYTE2, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0, &ioEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR0_SD_BYTE3, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &ioReady) != HAL_OK)
  {
    return HAL_ERROR;
  }
  /* check if IO ready */
  if ((ioReady & (1U << (uint32_t)IOFunction)) != 0U)
  {
    return HAL_OK;
  }

  return HAL_ERROR;
}

/**
  * @brief  Disable SDIO IO Enable.
  * @param  hsdio: Pointer to SDIO handle
  * @param  IOFunction: Specifies the SDIO IO function.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_DisableIOFunction(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  uint8_t ioEn  = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR0_SD_BYTE2, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &ioEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already enable , do not need enable again */
  if (((ioEn >> (uint32_t)IOFunction) & 0x01U) == 0x00U)
  {
    return HAL_OK;
  }
  else
  {
    ioEn &= ~(1U << (uint32_t)IOFunction);
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR0_SD_BYTE2, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0, &ioEn) != HAL_OK)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Select SDIO IO Enable.
  * @param  hsdio: Pointer to SDIO handle
  * @param  IOFunction: Specifies the SDIO IO function.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_SelectIOFunction(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR12_SD_BYTE1, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0,
                       (uint8_t *)&IOFunction) != HAL_OK)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Abort IO transfer.
  * @param  hsdio:  Pointer to SDIO handle
  * @param IOFunction IO number
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_AbortIOFunction(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction)
{
  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR4_SD_BYTE2, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0,
                       (uint8_t *)&IOFunction) != HAL_OK)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Enable Assynchrone interrupt.
  * @param  hsdio: Pointer to SDIO handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_EnableIOAsynInterrupt(SDIO_HandleTypeDef *hsdio)
{
  uint8_t enable_asyn_it  = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR20_SD_BYTE2, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &enable_asyn_it)
      != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already enable , do not need enable again */
  if ((enable_asyn_it & 0x02U) == 0x02U)
  {
    return HAL_OK;
  }
  else
  {
    enable_asyn_it |= 0x02U;
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR20_SD_BYTE2, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0,
                       &enable_asyn_it) != HAL_OK)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Disable Assynchrone interrupt.
  * @param  hsdio: Pointer to SDIO handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SDIO_DisableIOAsynInterrupt(SDIO_HandleTypeDef *hsdio)
{
  uint8_t enable_asyn_it  = 0U;

  /* Check the parameters */
  assert_param(hsdio != NULL);

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR20_SD_BYTE2, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &enable_asyn_it)
      != HAL_OK)
  {
    return HAL_ERROR;
  }

  /* if already disable , do not need disable again */
  if ((enable_asyn_it & 0x02U) == 0x00U)
  {
    return HAL_OK;
  }
  else
  {
    enable_asyn_it &= (uint8_t) ~(0x02U);
  }

  if (SDIO_WriteDirect(hsdio, SDMMC_SDIO_CCCR20_SD_BYTE2, HAL_SDIO_READ_AFTER_WRITE, SDIO_FUNCTION_0,
                       &enable_asyn_it) != HAL_OK)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief sdio set io IRQ handler.
  * @param hsdio  Pointer to SDIO handle
  * @param IOFunction IO function io number.
  * @param Callback io IRQ handler.
  */
HAL_StatusTypeDef HAL_SDIO_RegisterIOFunctionCallback(SDIO_HandleTypeDef *hsdio, uint32_t IOFunction,
                                                      HAL_SDIO_IOFunction_CallbackTypeDef pCallback)
{
  /* Check the parameters */
  assert_param(hsdio != NULL);
  assert_param(IS_SDIO_FUNCTION(IOFunction));

  /* Check the SDIO peripheral handle parameter */
  if (hsdio == NULL)
  {
    return HAL_ERROR;
  }

  hsdio->SDIO_IOFunction_Callback[(uint32_t)IOFunction] = pCallback;
  hsdio->IOFunctionMask |= (1U << (uint8_t)IOFunction);

  return HAL_OK;
}
/**
  * @}
  */

/**
  * @}
  */

/* Private function --------------------------------------------------------------------------------------------------*/
/** @addtogroup SDIO_Private_Functions
  * @{
  */
/**
  * @brief  Initializes the SDIO device.
  * @param  hsdio: Pointer to the SDIO handle
  * @retval HAL status
  */
static HAL_StatusTypeDef SDIO_InitCard(SDIO_HandleTypeDef *hsdio)
{
  uint32_t errorstate;
  uint32_t timeout = 0U;
  uint16_t sdio_rca = 1U;
  uint32_t Resp4;
  uint32_t nbr_of_func;

  /* Identify card operating voltage */
  errorstate = SDMMC_CmdGoIdleState(hsdio->Instance);
  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    return HAL_ERROR;
  }

  /* Check the power State */
  if (SDMMC_GetPowerState(hsdio->Instance) == 0U)
  {
    return HAL_ERROR;
  }

  /* Send CMD5 */
  errorstate = SDMMC_CmdSendOperationcondition(hsdio->Instance, 0U, &Resp4);
  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    return HAL_ERROR;
  }

  nbr_of_func = ((Resp4 & 0x70000000U) >> 28U);
  /* Check if Nbr of function > 0 and OCR valid */
  if (nbr_of_func > 0U)
  {
    /* Send CMD5 with arg= S18R, WV*/
    if (SDMMC_CmdSendOperationcondition(hsdio->Instance, (SDIO_OCR_VDD_32_33 | SDIO_OCR_SDIO_S18R), &Resp4)
        != HAL_SDIO_ERROR_NONE)
    {
      return HAL_ERROR;
    }
    /* Check if IORDY = 1 and S18A = 1 */
    if ((((Resp4 & 0x80000000U) >> 31U) != 0U) && (((Resp4 & 0x1000000U) >> 24U) != 0U))
    {
      /* Send CMD11 to switch 1.8V mode */
      errorstate = SDMMC_CmdVoltageSwitch(hsdio->Instance);
      if (errorstate != HAL_SDIO_ERROR_NONE)
      {
        return HAL_ERROR;
      }
    }
    else
    {
      /* S18A is not supported */
    }
  }

  /** Cmd3 is sent while response is SDMMC_ERROR_ILLEGAL_CMD, due to the partial init test done before
    * (sending cmd0 after the sequence cmd0/cmd3 is sent is considered illegal).
    */
  do
  {
    errorstate = SDMMC_CmdSetRelAdd(hsdio->Instance, &sdio_rca);
    timeout++;
    HAL_Delay(1);
  } while ((errorstate == SDMMC_ERROR_ILLEGAL_CMD) && (timeout != SDIO_TIMEOUT));

  if ((timeout == SDIO_TIMEOUT) || (errorstate != HAL_SDIO_ERROR_NONE))
  {
    return HAL_ERROR;
  }

  /* Select the Card ( Sending CMD7)*/
  errorstate = SDMMC_CmdSelDesel(hsdio->Instance, (uint32_t)(((uint32_t)sdio_rca) << 16U));
  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    return HAL_ERROR;
  }

  return HAL_OK;
}

/**
  * @brief  Read 1 byte data.
  * @param  hsdio: Pointer to SDIO handle
  * @param  cmd_arg: formatted CMD52 structure
  * @param  pData: pointer to write or read data
  * @retval HAL status
  */
static HAL_StatusTypeDef SDIO_ReadDirect(SDIO_HandleTypeDef *hsdio, uint32_t addr, uint32_t raw,
                                         uint32_t function_nbr, uint8_t *pData)
{
  uint32_t errorstate;
  uint32_t cmd;

  cmd = SDIO_READ << 31U;
  cmd |= function_nbr << 28U;
  cmd |= raw << 27U;
  cmd |= (addr & 0x1FFFFU) << 9U;
  errorstate = SDMMC_SDIO_CmdReadWriteDirect(hsdio->Instance, cmd, pData);
  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    hsdio->ErrorCode |= errorstate;
    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);

  /* Clear all the static flags */
  __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

  return HAL_OK;
}

/**
  * @brief  Write 1 byte data.
  * @param  hsdio: Pointer to SDIO handle
  * @param  cmd_arg: formatted CMD52 structure
  * @param  pData: pointer to write or read data
  * @retval HAL status
  */
static HAL_StatusTypeDef SDIO_WriteDirect(SDIO_HandleTypeDef *hsdio, uint32_t addr, uint32_t raw,
                                          uint32_t function_nbr, uint8_t *pData)
{
  uint32_t errorstate;
  uint32_t cmd;
  uint8_t response;

  cmd = SDIO_WRITE << 31U;
  cmd |= function_nbr << 28U;
  cmd |= raw << 27U;
  cmd |= (addr & 0x1FFFFU) << 9U;
  cmd |= ((uint32_t) * pData & 0x000000FFU);
  errorstate = SDMMC_SDIO_CmdReadWriteDirect(hsdio->Instance, cmd, &response);

  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    hsdio->ErrorCode |= errorstate;
    /* Clear all the static flags */
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);

  /* Clear all the static flags */
  __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

  return HAL_OK;
}

/**
  * @brief  Write multiple data with a single command.
  * @param  hsdio: Pointer to SDIO handle
  * @param  cmd_arg: formatted cmd53 structure
  * @param  Size_byte: block size if CMD53 defined in HAL_SDIO_MODE_BLOCK
  * @param  pData: pointer to write or read data
  * @retval HAL status
  */
static HAL_StatusTypeDef SDIO_WriteExtended(SDIO_HandleTypeDef *hsdio, HAL_SDIO_ExtendedCmd_TypeDef *cmd_arg,
                                            uint8_t *pData, uint16_t Size_byte)
{
  SDMMC_DataInitTypeDef config;
  uint32_t errorstate;
  uint32_t tickstart = HAL_GetTick();
  uint32_t regCount;
  uint8_t byteCount;
  uint32_t data;
  uint32_t dataremaining;
  uint32_t *u32tempbuff = (uint32_t *)(uint32_t)pData;
  SDMMC_TypeDef *SDMMCx;
  uint32_t cmd;
  uint32_t nbr_of_block;

  hsdio->ErrorCode = HAL_SDIO_ERROR_NONE;

  /* Compute how many blocks are to be send for pData of length data_size to be send */
  nbr_of_block = (((uint32_t)Size_byte & ~((uint32_t)hsdio->block_size & 1U))) >> __CLZ(__RBIT(hsdio->block_size));

  /* Initialize data control register */
  if ((hsdio->Instance->DCTRL & SDMMC_DCTRL_SDIOEN) != 0U)
  {
    hsdio->Instance->DCTRL = SDMMC_DCTRL_SDIOEN;
  }
  else
  {
    hsdio->Instance->DCTRL = 0U;
  }

  /* Configure the SDIO DPSM (Data Path State Machine) */
  config.DataTimeOut   = SDMMC_DATATIMEOUT;
  if (cmd_arg->Block_Mode == HAL_SDIO_MODE_BLOCK)
  {
    config.DataLength    = (uint32_t)(nbr_of_block * hsdio->block_size);
    config.DataBlockSize = SDIO_Convert_Block_Size(hsdio, hsdio->block_size);
  }
  else
  {
    config.DataLength    = Size_byte;
    config.DataBlockSize = SDMMC_DATABLOCK_SIZE_1B;
  }

  config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
  config.TransferMode  = (cmd_arg->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDMMC_TRANSFER_MODE_BLOCK :
                         SDMMC_TRANSFER_MODE_SDIO;
  config.DPSM          = SDMMC_DPSM_DISABLE;
  (void)SDMMC_ConfigData(hsdio->Instance, &config);
  __SDMMC_CMDTRANS_ENABLE(hsdio->Instance);

  hsdio->Context = (cmd_arg->Block_Mode == HAL_SDIO_MODE_BLOCK) ? SDIO_CONTEXT_WRITE_MULTIPLE_BLOCK :
                   SDIO_CONTEXT_WRITE_SINGLE_BLOCK;
  cmd = SDIO_WRITE << 31U;
  cmd |= cmd_arg->IOFunctionNbr << 28U;
  cmd |= cmd_arg->Block_Mode << 27U;
  cmd |= cmd_arg->OpCode << 26U;
  cmd |= (cmd_arg->Reg_Addr & 0x1FFFFU) << 9U;
  cmd |= (((uint32_t)Size_byte) & 0x1FFU);
  errorstate = SDMMC_SDIO_CmdReadWriteExtended(hsdio->Instance, cmd);
  if (errorstate != HAL_SDIO_ERROR_NONE)
  {
    MODIFY_REG(hsdio->Instance->DCTRL, SDMMC_DCTRL_FIFORST, SDMMC_DCTRL_FIFORST);
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
    hsdio->ErrorCode |= errorstate;
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }

  SDMMCx = hsdio->Instance;
  dataremaining = config.DataLength;
  while (!__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXUNDERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT |
                              SDMMC_FLAG_DATAEND))
  {
    if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXFIFOHE) && (dataremaining >= 32U))
    {
      for (regCount = 8U; regCount > 0U; regCount--)
      {
        SDMMCx->FIFO = *u32tempbuff;
        u32tempbuff++;
      }
      dataremaining -= 32U;
    }
    else if ((dataremaining < 32U) && (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXFIFOHE | SDMMC_FLAG_TXFIFOE)))
    {
      uint8_t *u8buff = (uint8_t *)u32tempbuff;
      while (dataremaining > 0U)
      {
        data = 0U;
        for (byteCount = 0U; (byteCount < 4U) && (dataremaining > 0U); byteCount++)
        {
          data |= ((uint32_t)(*u8buff) << (byteCount << 3U));
          u8buff++;
          dataremaining--;
        }
        SDMMCx->FIFO = data;
      }
    }
    if ((HAL_GetTick() - tickstart) >= SDMMC_DATATIMEOUT)
    {
      __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
      hsdio->ErrorCode |= HAL_SDIO_ERROR_TIMEOUT;
      hsdio->State = HAL_SDIO_STATE_READY;
      hsdio->Context = SDIO_CONTEXT_NONE;
      return HAL_TIMEOUT;
    }
  }

  __SDMMC_CMDTRANS_DISABLE(hsdio->Instance);
  if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DTIMEOUT))
  {
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_TIMEOUT;
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_DCRCFAIL))
  {
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    hsdio->ErrorCode |= HAL_SDIO_ERROR_DATA_CRC_FAIL;
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  else if (__HAL_SDIO_GET_FLAG(hsdio, SDMMC_FLAG_TXUNDERR))
  {
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_FLAGS);
    hsdio->ErrorCode |= HAL_SDIO_ERROR_TX_UNDERRUN;
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  else if (hsdio->ErrorCode == SDMMC_ERROR_INVALID_PARAMETER)
  {
    __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);
    hsdio->State = HAL_SDIO_STATE_READY;
    hsdio->Context = SDIO_CONTEXT_NONE;
    return HAL_ERROR;
  }
  else
  {
    /* Nothing to do */
  }

  __HAL_SDIO_CLEAR_FLAG(hsdio, SDMMC_STATIC_DATA_FLAGS);

  return HAL_OK;
}

/**
  * @brief  Allows to convert a block size in the according SDMMC value for configuring the SDMMC when doing a CMD53
  * @param  hsdio: Pointer to the SDIO handle.
  * @param  block_size: block size in bytes
  * @retval block size as DBLOCKSIZE[3:0] bits format
  */
static uint8_t SDIO_Convert_Block_Size(SDIO_HandleTypeDef *hsdio, uint32_t block_size)
{
  UNUSED(hsdio);

  uint8_t most_bit = (uint8_t)__CLZ(__RBIT(block_size));
  /*(1 << most_bit) - 1) is the mask used for blocksize*/
  if (((uint8_t)block_size & ((1U << most_bit) - 1U)) != 0U)
  {
    return (uint8_t)SDMMC_DATABLOCK_SIZE_4B;
  }
  return most_bit << SDMMC_DCTRL_DBLOCKSIZE_Pos;
}

/*!
 * @brief SDIO card io pending interrupt handle function.
 * @note  This function is used to handle the pending io interrupt.
 *        To register a IO IRQ handler, Use HAL_SDIO_EnableIOInterrupt and HAL_SDIO_SetIOIRQHandler
 * @param  hsdio: Pointer to SDIO handle
 * @retval HAL status
 */
static HAL_StatusTypeDef SDIO_IOFunction_IRQHandler(SDIO_HandleTypeDef *hsdio)
{
  uint8_t count;
  uint8_t pendingInt;

  if (hsdio->IOInterruptNbr == 1U)
  {
    if ((hsdio->SDIO_IOFunction_Callback[hsdio->IOFunctionMask - 1U]) != NULL)
    {
      (hsdio->SDIO_IOFunction_Callback[hsdio->IOFunctionMask - 1U])(hsdio, hsdio->IOFunctionMask - 1U);
    }
  }
  else if ((hsdio->IOInterruptNbr > 1U) && (hsdio->IOFunctionMask != 0U))
  {
    /* Get pending int firstly */
    if (SDIO_ReadDirect(hsdio, SDMMC_SDIO_CCCR4_SD_BYTE1, HAL_SDIO_WRITE_ONLY, SDIO_FUNCTION_0, &pendingInt) !=
        HAL_OK)
    {
      return HAL_ERROR;
    }

    if ((pendingInt != 0U) && (hsdio->IOFunctionMask != 0U))
    {
      for (count = 1; count <= SDIO_MAX_IO_NUMBER; count++)
      {
        if (((pendingInt & (1U << count)) != 0U) && (((1U << count)  & hsdio->IOFunctionMask) != 0U))
        {
          if ((hsdio->SDIO_IOFunction_Callback[count - 1U]) != NULL)
          {
            (hsdio->SDIO_IOFunction_Callback[count - 1U])(hsdio, count);
          }
        }
      }
    }
  }
  else
  {
    /* Nothing to do */
  }

  return HAL_OK;
}
/**
  * @}
  */
#endif /* HAL_SDIO_MODULE_ENABLED */
#endif /* SDMMC1 || SDMMC2 */
/**
  * @}
  */

/**
  * @}
  */
