From 366700747d365973d3e5c38912f18717f88012aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20M=C3=BAdry?= Date: Thu, 12 Mar 2026 16:23:22 +0100 Subject: [PATCH] feat(sdmmc): Try to reallocate smaller DMA buffer if larger one failed --- .../include/driver/sdmmc_default_configs.h | 2 +- .../include/driver/sdspi_host.h | 2 +- components/sdmmc/sdmmc_cmd.c | 39 +++++++++++++++---- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h b/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h index 29ba1ed08b..9a588cf8d0 100644 --- a/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h +++ b/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h @@ -48,7 +48,7 @@ extern "C" { .input_delay_phase = SDMMC_DELAY_PHASE_0, \ .set_input_delay = &sdmmc_host_set_input_delay, \ .set_input_delayline = &sdmmc_host_set_input_delayline, \ - .unaligned_multi_block_rw_max_chunk_size = 0, \ + .unaligned_multi_block_rw_max_chunk_size = 16, \ .dma_aligned_buffer = NULL, \ .pwr_ctrl_handle = NULL, \ .check_buffer_alignment = &sdmmc_host_check_buffer_alignment, \ diff --git a/components/esp_driver_sdspi/include/driver/sdspi_host.h b/components/esp_driver_sdspi/include/driver/sdspi_host.h index 16dd99f396..76d112e3d0 100644 --- a/components/esp_driver_sdspi/include/driver/sdspi_host.h +++ b/components/esp_driver_sdspi/include/driver/sdspi_host.h @@ -61,7 +61,7 @@ typedef int sdspi_dev_handle_t; .input_delay_phase = SDMMC_DELAY_PHASE_0, \ .set_input_delay = NULL, \ .set_input_delayline = NULL, \ - .unaligned_multi_block_rw_max_chunk_size = 0, \ + .unaligned_multi_block_rw_max_chunk_size = 16, \ .dma_aligned_buffer = NULL, \ .pwr_ctrl_handle = NULL, \ .check_buffer_alignment = sdspi_host_check_buffer_alignment, \ diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 61c7e91bc0..3b652ee393 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -23,6 +23,27 @@ static inline size_t get_chunk_size(const sdmmc_card_t *card) return (chunk_size != 0) ? chunk_size : 1; } +static esp_err_t allocate_dma_buf(size_t* actual_size, size_t block_size, void **buf) +{ + if (actual_size == NULL || buf == NULL) { + return ESP_ERR_INVALID_ARG; + } + + size_t size = *actual_size; + do { + if (*actual_size < block_size) { + ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); + return ESP_ERR_NO_MEM; + } + *buf = heap_caps_malloc(*actual_size, MALLOC_CAP_DMA); + if (!*buf) { + *actual_size /= 2; + ESP_LOGD(TAG, "%s: required space for buffer of size %d not available, trying again with size %zu", __func__, size, *actual_size); + } + } while (!*buf); + return ESP_OK; +} + esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd) { if (card->host.command_timeout_ms != 0) { @@ -489,11 +510,12 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, // Allocate a temporary DMA-capable buffer. // We don't want to force the allocation into SPIRAM, the allocator // will decide based on the buffer size and memory availability. - buf = heap_caps_malloc(actual_size, MALLOC_CAP_DMA); - if (!buf) { - ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); - return ESP_ERR_NO_MEM; + // We start with the largest buffer possible to minimize the number of read iterations, but if that fails, we try smaller sizes down to a single block. + err = allocate_dma_buf(&actual_size, block_size, &buf); + if (err != ESP_OK) { + return err; } + blocks_per_write = actual_size / block_size; } else { // Check that the provided dma_aligned_buffer is large enough actual_size = heap_caps_get_allocated_size(buf); @@ -649,11 +671,12 @@ esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, // Allocate a temporary DMA-capable buffer. // We don't want to force the allocation into SPIRAM, the allocator // will decide based on the buffer size and memory availability. - buf = heap_caps_malloc(actual_size, MALLOC_CAP_DMA); - if (!buf) { - ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); - return ESP_ERR_NO_MEM; + // We start with the largest buffer possible to minimize the number of read iterations, but if that fails, we try smaller sizes down to a single block. + err = allocate_dma_buf(&actual_size, block_size, &buf); + if (err != ESP_OK) { + return err; } + blocks_per_read = actual_size / block_size; } else { // Check that the provided dma_aligned_buffer is large enough actual_size = heap_caps_get_allocated_size(buf);