/**
  * @brief  Initializes the SD MSP.
  * @retval None
  */
static void SD_MspInit(void)
{
  GPIO_InitTypeDef gpioinitstruct = {0};
  
  /* Enable SDIO clock */
  __HAL_RCC_SDIO_CLK_ENABLE();
  
  /* Enable DMA2 clocks */
  __DMAx_TxRx_CLK_ENABLE();

  /* Enable GPIOs clock */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __SD_DETECT_GPIO_CLK_ENABLE();
  
  /* Common GPIO configuration */
  gpioinitstruct.Mode      = GPIO_MODE_AF_PP;
  gpioinitstruct.Pull      = GPIO_PULLUP;
  gpioinitstruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
  gpioinitstruct.Alternate = GPIO_AF12_SDIO;
  
  /* GPIOC configuration */
  gpioinitstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
   
  HAL_GPIO_Init(GPIOC, &gpioinitstruct);

  /* GPIOD configuration */
  gpioinitstruct.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpioinitstruct);

  /* SD Card detect pin configuration */
  gpioinitstruct.Mode      = GPIO_MODE_INPUT;
  gpioinitstruct.Pull      = GPIO_PULLUP;
  gpioinitstruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
  gpioinitstruct.Pin       = SD_DETECT_PIN;
  HAL_GPIO_Init(SD_DETECT_GPIO_PORT, &gpioinitstruct);
    
  /* NVIC configuration for SDIO interrupts */
  HAL_NVIC_SetPriority(SDIO_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(SDIO_IRQn);
  
  /* DMA initialization should be done here but , as there is only one channel for RX and TX it is configured and done directly when required*/
}
/**
  * @brief  Initializes the SD MSP.
  * @param  hsd: SD handle
  * @param  Params : pointer on additional configuration parameters, can be NULL.
  */
__weak void BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params)
{
  static DMA_HandleTypeDef dma_rx_handle;
  static DMA_HandleTypeDef dma_tx_handle;
  GPIO_InitTypeDef gpio_init_structure;

  /* Enable SDIO clock */
  __HAL_RCC_SDIO_CLK_ENABLE();

  /* Enable DMA2 clocks */
  __DMAx_TxRx_CLK_ENABLE();

  /* Enable GPIOs clock */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /* Common GPIO configuration */
  gpio_init_structure.Mode      = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull      = GPIO_PULLUP;
  gpio_init_structure.Speed     = GPIO_SPEED_HIGH;
  gpio_init_structure.Alternate = GPIO_AF12_SDIO;

  /* GPIOC configuration */
  gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;

  HAL_GPIO_Init(GPIOC, &gpio_init_structure);

  /* GPIOD configuration */
  gpio_init_structure.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpio_init_structure);

  /* NVIC configuration for SDIO interrupts */
  //HAL_NVIC_SetPriority(SDIO_IRQn, 5, 0);
  //HAL_NVIC_EnableIRQ(SDIO_IRQn);

  /* Configure DMA Rx parameters */
  dma_rx_handle.Init.Channel             = SD_DMAx_Rx_CHANNEL;
  dma_rx_handle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
  dma_rx_handle.Init.PeriphInc           = DMA_PINC_DISABLE;
  dma_rx_handle.Init.MemInc              = DMA_MINC_ENABLE;
  dma_rx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  dma_rx_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  dma_rx_handle.Init.Mode                = DMA_PFCTRL;
  dma_rx_handle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
  dma_rx_handle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
  dma_rx_handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  dma_rx_handle.Init.MemBurst            = DMA_MBURST_INC4;
  dma_rx_handle.Init.PeriphBurst         = DMA_PBURST_INC4;

  dma_rx_handle.Instance = SD_DMAx_Rx_STREAM;

  /* Associate the DMA handle */
  __HAL_LINKDMA(hsd, hdmarx, dma_rx_handle);

  /* Deinitialize the stream for new transfer */
  HAL_DMA_DeInit(&dma_rx_handle);

  /* Configure the DMA stream */
  HAL_DMA_Init(&dma_rx_handle);

  /* Configure DMA Tx parameters */
  dma_tx_handle.Init.Channel             = SD_DMAx_Tx_CHANNEL;
  dma_tx_handle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  dma_tx_handle.Init.PeriphInc           = DMA_PINC_DISABLE;
  dma_tx_handle.Init.MemInc              = DMA_MINC_ENABLE;
  dma_tx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  dma_tx_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  dma_tx_handle.Init.Mode                = DMA_PFCTRL;
  dma_tx_handle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
  dma_tx_handle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
  dma_tx_handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  dma_tx_handle.Init.MemBurst            = DMA_MBURST_INC4;
  dma_tx_handle.Init.PeriphBurst         = DMA_PBURST_INC4;

  dma_tx_handle.Instance = SD_DMAx_Tx_STREAM;

  /* Associate the DMA handle */
  __HAL_LINKDMA(hsd, hdmatx, dma_tx_handle);

  /* Deinitialize the stream for new transfer */
  HAL_DMA_DeInit(&dma_tx_handle);

  /* Configure the DMA stream */
  HAL_DMA_Init(&dma_tx_handle);

  /* NVIC configuration for DMA transfer complete interrupt */
  HAL_NVIC_SetPriority(SD_DMAx_Rx_IRQn, 6, 0);
  HAL_NVIC_EnableIRQ(SD_DMAx_Rx_IRQn);

  /* NVIC configuration for DMA transfer complete interrupt */
  HAL_NVIC_SetPriority(SD_DMAx_Tx_IRQn, 6, 0);
  HAL_NVIC_EnableIRQ(SD_DMAx_Tx_IRQn);
}
/**
  * @brief  Initializes the SD MSP.
  * @param  hsd: SD handle
  * @retval None
  */
__weak void BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params)
{
  static DMA_HandleTypeDef dma_rx_handle;
  static DMA_HandleTypeDef dma_tx_handle;
  static DMA_HandleTypeDef dma_rx_handle2;
  static DMA_HandleTypeDef dma_tx_handle2;  
  GPIO_InitTypeDef gpio_init_structure;
  
  /* Camera has to be powered down as some signals use same GPIOs between 
  * SD card and camera bus. Camera drives its signals to low impedance 
  * when powered ON. So the camera is powered off to let its signals
  * in high impedance */
  
  /* Camera power down sequence */
  BSP_IO_ConfigPin(RSTI_PIN, IO_MODE_OUTPUT);
  BSP_IO_ConfigPin(XSDN_PIN, IO_MODE_OUTPUT);
  /* De-assert the camera STANDBY pin (active high) */
  BSP_IO_WritePin(XSDN_PIN, BSP_IO_PIN_RESET);
  /* Assert the camera RSTI pin (active low) */
  BSP_IO_WritePin(RSTI_PIN, BSP_IO_PIN_RESET);
  if(hsd->Instance == SDMMC1)
  {
    /* Enable SDIO clock */
    __HAL_RCC_SDMMC1_CLK_ENABLE();
    
    /* Enable DMA2 clocks */
    __DMAx_TxRx_CLK_ENABLE();
    
    /* Enable GPIOs clock */
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    
    /* Common GPIO configuration */
    gpio_init_structure.Mode      = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull      = GPIO_PULLUP;
    gpio_init_structure.Speed     = GPIO_SPEED_HIGH;
    gpio_init_structure.Alternate = GPIO_AF12_SDMMC1;
    
    /* GPIOC configuration: SD1_D0, SD1_D1, SD1_D2, SD1_D3 and SD1_CLK pins */
    gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
    
    HAL_GPIO_Init(GPIOC, &gpio_init_structure);
    
    /* GPIOD configuration: SD1_CMD pin */
    gpio_init_structure.Pin = GPIO_PIN_2;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);
    
    /* NVIC configuration for SDIO interrupts */
    HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
  
    dma_rx_handle.Init.Channel             = SD1_DMAx_Rx_CHANNEL;
    dma_rx_handle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    dma_rx_handle.Init.PeriphInc           = DMA_PINC_DISABLE;
    dma_rx_handle.Init.MemInc              = DMA_MINC_ENABLE;
    dma_rx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_rx_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
    dma_rx_handle.Init.Mode                = DMA_PFCTRL;
    dma_rx_handle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    dma_rx_handle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    dma_rx_handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    dma_rx_handle.Init.MemBurst            = DMA_MBURST_INC4;
    dma_rx_handle.Init.PeriphBurst         = DMA_PBURST_INC4;
    dma_rx_handle.Instance                 = SD1_DMAx_Rx_STREAM;
    
    /* Associate the DMA handle */
    __HAL_LINKDMA(hsd, hdmarx, dma_rx_handle);
    
    /* Deinitialize the stream for new transfer */
    HAL_DMA_DeInit(&dma_rx_handle);
    
    /* Configure the DMA stream */    
    HAL_DMA_Init(&dma_rx_handle);
         
    dma_tx_handle.Init.Channel             = SD1_DMAx_Tx_CHANNEL;
    dma_tx_handle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    dma_tx_handle.Init.PeriphInc           = DMA_PINC_DISABLE;
    dma_tx_handle.Init.MemInc              = DMA_MINC_ENABLE;
    dma_tx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_tx_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
    dma_tx_handle.Init.Mode                = DMA_PFCTRL;
    dma_tx_handle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    dma_tx_handle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    dma_tx_handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    dma_tx_handle.Init.MemBurst            = DMA_MBURST_INC4;
    dma_tx_handle.Init.PeriphBurst         = DMA_PBURST_INC4;
    dma_tx_handle.Instance                 = SD1_DMAx_Tx_STREAM; 
    
    /* Associate the DMA handle */
    __HAL_LINKDMA(hsd, hdmatx, dma_tx_handle);
    
    /* Deinitialize the stream for new transfer */
    HAL_DMA_DeInit(&dma_tx_handle);
    
    /* Configure the DMA stream */
    HAL_DMA_Init(&dma_tx_handle);  

    /* NVIC configuration for DMA transfer complete interrupt */
    HAL_NVIC_SetPriority(SD1_DMAx_Rx_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(SD1_DMAx_Rx_IRQn);
    
    /* NVIC configuration for DMA transfer complete interrupt */
    HAL_NVIC_SetPriority(SD1_DMAx_Tx_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(SD1_DMAx_Tx_IRQn);  
  }
  else
  {
    /* Enable SDIO clock */
    __HAL_RCC_SDMMC2_CLK_ENABLE();
    
    /* Enable DMA2 clocks */
    __DMAx_TxRx_CLK_ENABLE();
    
    /* Enable GPIOs clock */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    
    /* Common GPIO configuration */
    gpio_init_structure.Mode      = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull      = GPIO_PULLUP;
    gpio_init_structure.Speed     = GPIO_SPEED_HIGH;
    gpio_init_structure.Alternate = GPIO_AF10_SDMMC2;
    
    /* GPIOB configuration: SD2_D2 and SD2_D3 pins */
    gpio_init_structure.Pin = GPIO_PIN_3 | GPIO_PIN_4;
    
    HAL_GPIO_Init(GPIOB, &gpio_init_structure);
    
    /* GPIOD configuration: SD2_CLK and SD2_CMD pins */
    gpio_init_structure.Pin = GPIO_PIN_6 | GPIO_PIN_7;
    gpio_init_structure.Alternate = GPIO_AF11_SDMMC2;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);
    
    /* GPIOG configuration: SD2_D0 and SD2_D1 pins */
    gpio_init_structure.Pin = GPIO_PIN_9 | GPIO_PIN_10;
    
    HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    
    /* NVIC configuration for SDIO interrupts */
    HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(SDMMC2_IRQn);
    
   
    dma_rx_handle2.Init.Channel             = SD2_DMAx_Rx_CHANNEL;
    dma_rx_handle2.Init.Direction           = DMA_PERIPH_TO_MEMORY;
    dma_rx_handle2.Init.PeriphInc           = DMA_PINC_DISABLE;
    dma_rx_handle2.Init.MemInc              = DMA_MINC_ENABLE;
    dma_rx_handle2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_rx_handle2.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
    dma_rx_handle2.Init.Mode                = DMA_PFCTRL;
    dma_rx_handle2.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    dma_rx_handle2.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    dma_rx_handle2.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    dma_rx_handle2.Init.MemBurst            = DMA_MBURST_INC4;
    dma_rx_handle2.Init.PeriphBurst         = DMA_PBURST_INC4;
    dma_rx_handle2.Instance                 = SD2_DMAx_Rx_STREAM;     
    
    /* Associate the DMA handle */
    __HAL_LINKDMA(hsd, hdmarx, dma_rx_handle2);
    
    /* Deinitialize the stream for new transfer */
    HAL_DMA_DeInit(&dma_rx_handle2);
    
    /* Configure the DMA stream */    
    HAL_DMA_Init(&dma_rx_handle2);

    dma_tx_handle2.Init.Channel             = SD2_DMAx_Tx_CHANNEL;
    dma_tx_handle2.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    dma_tx_handle2.Init.PeriphInc           = DMA_PINC_DISABLE;
    dma_tx_handle2.Init.MemInc              = DMA_MINC_ENABLE;
    dma_tx_handle2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_tx_handle2.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
    dma_tx_handle2.Init.Mode                = DMA_PFCTRL;
    dma_tx_handle2.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
    dma_tx_handle2.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    dma_tx_handle2.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
    dma_tx_handle2.Init.MemBurst            = DMA_MBURST_INC4;
    dma_tx_handle2.Init.PeriphBurst         = DMA_PBURST_INC4;
    dma_tx_handle2.Instance                 = SD2_DMAx_Tx_STREAM;    
    
    /* Associate the DMA handle */
    __HAL_LINKDMA(hsd, hdmatx, dma_tx_handle2);
    
    /* Deinitialize the stream for new transfer */
    HAL_DMA_DeInit(&dma_tx_handle2);
    
    /* Configure the DMA stream */
    HAL_DMA_Init(&dma_tx_handle2);  
 
    /* NVIC configuration for DMA transfer complete interrupt */
    HAL_NVIC_SetPriority(SD2_DMAx_Rx_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(SD2_DMAx_Rx_IRQn);
    
    /* NVIC configuration for DMA transfer complete interrupt */
    HAL_NVIC_SetPriority(SD2_DMAx_Tx_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(SD2_DMAx_Tx_IRQn);     
  }
}
/**
  * @brief  Initializes the SD card device.
  * @param  None
  * @retval SD status
  */
uint8_t BSP_SD_Init(void) { 
	uint8_t SD_state = MSD_OK;

	/* uSD device interface configuration */
#if defined(SDIO)
	uSdHandle.Instance = SDIO;

	uSdHandle.Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;
	uSdHandle.Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;
	uSdHandle.Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;
	uSdHandle.Init.BusWide             = SDIO_BUS_WIDE_1B;
	uSdHandle.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
	uSdHandle.Init.ClockDiv            = SDIO_TRANSFER_CLK_DIV;
#elif defined(SDMMC1)
	uSdHandle.Instance = SDMMC1;

	uSdHandle.Init.ClockEdge           = SDMMC_CLOCK_EDGE_RISING;
	uSdHandle.Init.ClockBypass         = SDMMC_CLOCK_BYPASS_DISABLE;
	uSdHandle.Init.ClockPowerSave      = SDMMC_CLOCK_POWER_SAVE_DISABLE;
	uSdHandle.Init.BusWide             = SDMMC_BUS_WIDE_1B;
	uSdHandle.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
	uSdHandle.Init.ClockDiv            = SDMMC_TRANSFER_CLK_DIV;	
#else
#error "NOT SUPPORTED!"
#endif

	/* Init GPIO, DMA and NVIC */
	SD_MspInit();

	/* Check if the SD card is plugged in the slot */
	if (BSP_SD_IsDetected() != SD_PRESENT) {
		return MSD_ERROR;
	}

	/* HAL SD initialization */
	if (HAL_SD_Init(&uSdHandle, &uSdCardInfo) != SD_OK) {
		SD_state = MSD_ERROR;
	}

	/* Configure SD Bus width */
	if (SD_state == MSD_OK) {
		/* Enable wide operation */
#if defined(SDIO_BUS_WIDE_4B)
#if FATFS_SDIO_4BIT == 1
		if (HAL_SD_WideBusOperation_Config(&uSdHandle, SDIO_BUS_WIDE_4B) != SD_OK) {
#else
		if (HAL_SD_WideBusOperation_Config(&uSdHandle, SDIO_BUS_WIDE_1B) != SD_OK) {	
#endif
#else
#if FATFS_SDIO_4BIT == 1
		if (HAL_SD_WideBusOperation_Config(&uSdHandle, SDMMC_BUS_WIDE_4B) != SD_OK) {
#else
		if (HAL_SD_WideBusOperation_Config(&uSdHandle, SDMMC_BUS_WIDE_1B) != SD_OK) {	
#endif
#endif
			SD_state = MSD_ERROR;
		} else {
			SD_state = MSD_OK;
		}
	}

	return  SD_state;
}

/**
 * @brief  Detects if SD card is correctly plugged in the memory slot or not.
 * @param  None
 * @retval Returns if SD is detected or not
 */
uint8_t BSP_SD_IsDetected(void) {
	return SDCARD_IsDetected();
}

/**
 * @brief  Detects if SD card is write protected
 * @param  None
 * @retval Returns if SD is write protected or not.
 */
uint8_t BSP_SD_IsWriteProtected(void) {
	return !SDCARD_IsWriteEnabled();
}

/**
  * @brief  Reads block(s) from a specified address in an SD card, in polling mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  ReadAddr: Address from where data is to be read  
  * @param  BlockSize: SD card data block size, that should be 512
  * @param  NumOfBlocks: Number of SD blocks to read 
  * @retval SD status
  */
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint64_t ReadAddr, uint32_t BlockSize, uint32_t NumOfBlocks) {
	if (HAL_SD_ReadBlocks(&uSdHandle, pData, ReadAddr, BlockSize, NumOfBlocks) != SD_OK) {
		return MSD_ERROR;
	}
	
	return MSD_OK;
}

/**
  * @brief  Writes block(s) to a specified address in an SD card, in polling mode. 
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  WriteAddr: Address from where data is to be written  
  * @param  BlockSize: SD card data block size, that should be 512
  * @param  NumOfBlocks: Number of SD blocks to write
  * @retval SD status
  */
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks) {
	if (HAL_SD_WriteBlocks(&uSdHandle, pData, WriteAddr, BlockSize, NumOfBlocks) != SD_OK) {
		return MSD_ERROR;
	}
	
	return MSD_OK;
}

/**
  * @brief  Reads block(s) from a specified address in an SD card, in DMA mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  ReadAddr: Address from where data is to be read  
  * @param  BlockSize: SD card data block size, that should be 512
  * @param  NumOfBlocks: Number of SD blocks to read 
  * @retval SD status
  */
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint64_t ReadAddr, uint32_t BlockSize, uint32_t NumOfBlocks) {
	uint8_t SD_state = MSD_OK;

	/* Read block(s) in DMA transfer mode */
	if (HAL_SD_ReadBlocks_DMA(&uSdHandle, pData, ReadAddr, BlockSize, NumOfBlocks) != SD_OK) {
		SD_state = MSD_ERROR;
	}

	/* Wait until transfer is complete */
	if (SD_state == MSD_OK) {
		if (HAL_SD_CheckReadOperation(&uSdHandle, (uint32_t)SD_DATATIMEOUT) != SD_OK) {
			SD_state = MSD_ERROR;
		} else {
			SD_state = MSD_OK;
		}
	}

	return SD_state; 
}

/**
  * @brief  Writes block(s) to a specified address in an SD card, in DMA mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  WriteAddr: Address from where data is to be written  
  * @param  BlockSize: SD card data block size, that should be 512
  * @param  NumOfBlocks: Number of SD blocks to write 
  * @retval SD status
  */
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks) {
	uint8_t SD_state = MSD_OK;

	/* Write block(s) in DMA transfer mode */
	if (HAL_SD_WriteBlocks_DMA(&uSdHandle, pData, WriteAddr, BlockSize, NumOfBlocks) != SD_OK) {
		SD_state = MSD_ERROR;
	}

	/* Wait until transfer is complete */
	if (SD_state == MSD_OK) {
		if(HAL_SD_CheckWriteOperation(&uSdHandle, (uint32_t)SD_DATATIMEOUT) != SD_OK) {
			SD_state = MSD_ERROR;
		} else {
			SD_state = MSD_OK;
		}
	}

	return SD_state;  
}

/**
  * @brief  Erases the specified memory area of the given SD card. 
  * @param  StartAddr: Start byte address
  * @param  EndAddr: End byte address
  * @retval SD status
  */
uint8_t BSP_SD_Erase(uint64_t StartAddr, uint64_t EndAddr) {
	if (HAL_SD_Erase(&uSdHandle, StartAddr, EndAddr) != SD_OK) {
		return MSD_ERROR;
	}
	
	return MSD_OK;
}

/**
  * @brief  Initializes the SD MSP.
  * @param  None
  * @retval None
  */
static void SD_MspInit(void) {
	static DMA_HandleTypeDef dmaRxHandle;
	static DMA_HandleTypeDef dmaTxHandle;
	SD_HandleTypeDef *hsd = &uSdHandle;
	uint16_t gpio_af;
	
	/* Get GPIO alternate function */
#if defined(GPIO_AF12_SDIO)
	gpio_af = GPIO_AF12_SDIO;
#endif
#if defined(GPIO_AF12_SDMMC1)
	gpio_af = GPIO_AF12_SDMMC1;
#endif
	
	/* Enable SDIO clock */
	__HAL_RCC_SDIO_CLK_ENABLE();

	/* Enable DMA2 clocks */
	__DMAx_TxRx_CLK_ENABLE();

	/* Detect pin, write protect pin */
#if FATFS_USE_DETECT_PIN > 0
	TM_GPIO_Init(FATFS_DETECT_PORT, FATFS_DETECT_PIN, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Low);
#endif
#if FATFS_USE_WRITEPROTECT_PIN > 0
	TM_GPIO_Init(FATFS_WRITEPROTECT_PORT, FATFS_WRITEPROTECT_PIN, TM_GPIO_Mode_IN, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Low);
#endif
	
	/* SDIO/SDMMC pins */
#if FATFS_SDIO_4BIT == 1
	TM_GPIO_InitAlternate(GPIOC, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Fast, gpio_af);
#else
	TM_GPIO_InitAlternate(GPIOC, GPIO_PIN_8 | GPIO_PIN_12, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Fast, gpio_af);
#endif
	TM_GPIO_InitAlternate(GPIOD, GPIO_PIN_2, TM_GPIO_OType_PP, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Fast, gpio_af);

	/* NVIC configuration for SDIO interrupts */
	HAL_NVIC_SetPriority(SDIO_IRQn, 5, 0);
	HAL_NVIC_EnableIRQ(SDIO_IRQn);

	/* Configure DMA Rx parameters */
	dmaRxHandle.Init.Channel             = SD_DMAx_Rx_CHANNEL;
	dmaRxHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
	dmaRxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
	dmaRxHandle.Init.MemInc              = DMA_MINC_ENABLE;
	dmaRxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	dmaRxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
	dmaRxHandle.Init.Mode                = DMA_PFCTRL;
	dmaRxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
	dmaRxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
	dmaRxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	dmaRxHandle.Init.MemBurst            = DMA_MBURST_INC4;
	dmaRxHandle.Init.PeriphBurst         = DMA_PBURST_INC4;

	dmaRxHandle.Instance = SD_DMAx_Rx_STREAM;

	/* Associate the DMA handle */
	__HAL_LINKDMA(hsd, hdmarx, dmaRxHandle);

	/* Deinitialize the stream for new transfer */
	HAL_DMA_DeInit(&dmaRxHandle);

	/* Configure the DMA stream */
	HAL_DMA_Init(&dmaRxHandle);

	/* Configure DMA Tx parameters */
	dmaTxHandle.Init.Channel             = SD_DMAx_Tx_CHANNEL;
	dmaTxHandle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
	dmaTxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
	dmaTxHandle.Init.MemInc              = DMA_MINC_ENABLE;
	dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	dmaTxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
	dmaTxHandle.Init.Mode                = DMA_PFCTRL;
	dmaTxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
	dmaTxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
	dmaTxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	dmaTxHandle.Init.MemBurst            = DMA_MBURST_INC4;
	dmaTxHandle.Init.PeriphBurst         = DMA_PBURST_INC4;

	dmaTxHandle.Instance = SD_DMAx_Tx_STREAM;

	/* Associate the DMA handle */
	__HAL_LINKDMA(hsd, hdmatx, dmaTxHandle);

	/* Deinitialize the stream for new transfer */
	HAL_DMA_DeInit(&dmaTxHandle);

	/* Configure the DMA stream */
	HAL_DMA_Init(&dmaTxHandle); 

	/* NVIC configuration for DMA transfer complete interrupt */
	HAL_NVIC_SetPriority(SD_DMAx_Rx_IRQn, 6, 0);
	HAL_NVIC_EnableIRQ(SD_DMAx_Rx_IRQn);

	/* NVIC configuration for DMA transfer complete interrupt */
	HAL_NVIC_SetPriority(SD_DMAx_Tx_IRQn, 6, 0);
	HAL_NVIC_EnableIRQ(SD_DMAx_Tx_IRQn);
}


/**
  * @brief  Get SD information about specific SD card.
  * @param  CardInfo: Pointer to HAL_SD_CardInfoTypedef structure
  * @retval None 
  */
void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypedef *CardInfo) {
	/* Get SD card Information */
	HAL_SD_Get_CardInfo(&uSdHandle, CardInfo);
}