From 4814514c26e76bd86317a36f4e4a185389d8fc65 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 22 Jun 2026 11:33:10 +0800 Subject: [PATCH] fix(sdio_slave): align buffer size checks with descriptor limits Define per-target SDIO slave descriptor buffer limits in the LL layer and validate queued send buffers against the 4-byte aligned effective maximum. Update the public docs to describe the chip-dependent limit instead of hardcoding 4092 bytes. Co-authored-by: Cursor --- components/esp_driver_sdio/include/driver/sdio_slave.h | 6 ++++-- components/esp_driver_sdio/src/sdio_slave.c | 5 ++++- components/esp_hal_sd/esp32/include/hal/sdio_slave_ll.h | 3 +++ components/esp_hal_sd/esp32c5/include/hal/sdio_slave_ll.h | 3 +++ components/esp_hal_sd/esp32c6/include/hal/sdio_slave_ll.h | 3 +++ components/esp_hal_sd/esp32c61/include/hal/sdio_slave_ll.h | 3 +++ docs/en/api-reference/peripherals/sdio_slave.rst | 2 +- docs/zh_CN/api-reference/peripherals/sdio_slave.rst | 2 +- 8 files changed, 22 insertions(+), 5 deletions(-) diff --git a/components/esp_driver_sdio/include/driver/sdio_slave.h b/components/esp_driver_sdio/include/driver/sdio_slave.h index d9383d883db..548b7680092 100644 --- a/components/esp_driver_sdio/include/driver/sdio_slave.h +++ b/components/esp_driver_sdio/include/driver/sdio_slave.h @@ -27,7 +27,9 @@ typedef struct { ///< If buffer_size is too large, the space larger than the transaction length is left blank but still counts a buffer, and the buffers are easily run out. ///< Should be set according to length of data really transferred. ///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. - ///< Buffer size of the slave pre-defined between host and slave before communication. All receive buffer given to the driver should be larger than this. + ///< Buffer size of the slave pre-defined between host and slave before communication. It must not exceed the + ///< maximum size supported by a single SDIO slave DMA descriptor on the current chip, + ///< and all receive buffer given to the driver should be larger than this. sdio_event_cb_t event_cb; ///< when the host interrupts slave, this callback will be called with interrupt number (0-7). uint32_t flags; ///< Features to be enabled for the slave, combinations of ``SDIO_SLAVE_FLAG_*``. #define SDIO_SLAVE_FLAG_DAT2_DISABLED BIT(0) /**< It is required by the SD specification that all 4 data @@ -194,7 +196,7 @@ uint8_t* sdio_slave_recv_get_buf(sdio_slave_buf_handle_t handle, size_t *len_o); * ``sdio_slave_send_get_finished`` after the transaction is finished. * * @param addr Address for data to be sent. The buffer should be DMA capable and 32-bit aligned. - * @param len Length of the data, should not be longer than 4092 bytes (may support longer in the future). + * @param len Length of the data, should not exceed the maximum size supported by a single SDIO slave DMA descriptor on the current chip. * @param arg Argument to returned in ``sdio_slave_send_get_finished``. The argument can be used to indicate which transaction is done, * or as a parameter for a callback. Set to NULL if not needed. * @param wait Time to wait if the buffer is full. diff --git a/components/esp_driver_sdio/src/sdio_slave.c b/components/esp_driver_sdio/src/sdio_slave.c index 1418bb2c2eb..5a8cc17de16 100644 --- a/components/esp_driver_sdio/src/sdio_slave.c +++ b/components/esp_driver_sdio/src/sdio_slave.c @@ -94,6 +94,8 @@ The driver of FIFOs works as below: #include "driver/gpio.h" #include "driver/sdio_slave.h" +#define SDIO_SLAVE_DMA_DESC_MAX_BUF_SIZE_ALIGNED_DOWN (SDIO_SLAVE_LL_DMA_DESC_MAX_BUF_SIZE & ~0x3U) + #define SDIO_SLAVE_CHECK(res, str, ret_val) do { if(!(res)){\ SDIO_SLAVE_LOGE("%s", str);\ return ret_val;\ @@ -611,7 +613,8 @@ static void sdio_intr_send(void *arg) esp_err_t sdio_slave_send_queue(uint8_t *addr, size_t len, void *arg, uint32_t wait) { - SDIO_SLAVE_CHECK(len > 0 && len <= 4092, "length out of range: (0, 4092]", ESP_ERR_INVALID_ARG); + SDIO_SLAVE_CHECK(len > 0 && len <= SDIO_SLAVE_DMA_DESC_MAX_BUF_SIZE_ALIGNED_DOWN, + "length out of range for a single DMA descriptor", ESP_ERR_INVALID_ARG); SDIO_SLAVE_CHECK(esp_ptr_dma_capable(addr) && (uint32_t)addr % 4 == 0, "buffer to send should be DMA capable and 32-bit aligned", ESP_ERR_INVALID_ARG); diff --git a/components/esp_hal_sd/esp32/include/hal/sdio_slave_ll.h b/components/esp_hal_sd/esp32/include/hal/sdio_slave_ll.h index aacecbd8576..2e96fb96110 100644 --- a/components/esp_hal_sd/esp32/include/hal/sdio_slave_ll.h +++ b/components/esp_hal_sd/esp32/include/hal/sdio_slave_ll.h @@ -67,6 +67,9 @@ typedef struct sdio_slave_ll_desc_s { }; } sdio_slave_ll_desc_t; +/* Maximum buffer size that a single SDIO slave DMA descriptor can point to. */ +#define SDIO_SLAVE_LL_DMA_DESC_MAX_BUF_SIZE ((1 << 12) - 1) + /// Mask of general purpose interrupts sending from the host. typedef enum { SDIO_SLAVE_LL_SLVINT_0 = BIT(0), ///< General purpose interrupt bit 0. diff --git a/components/esp_hal_sd/esp32c5/include/hal/sdio_slave_ll.h b/components/esp_hal_sd/esp32c5/include/hal/sdio_slave_ll.h index 069bf2ee106..73be6b5d8cc 100644 --- a/components/esp_hal_sd/esp32c5/include/hal/sdio_slave_ll.h +++ b/components/esp_hal_sd/esp32c5/include/hal/sdio_slave_ll.h @@ -67,6 +67,9 @@ typedef struct sdio_slave_ll_desc_s { }; } sdio_slave_ll_desc_t; +/* Maximum buffer size that a single SDIO slave DMA descriptor can point to. */ +#define SDIO_SLAVE_LL_DMA_DESC_MAX_BUF_SIZE ((1 << 14) - 1) + /// Mask of general purpose interrupts sending from the host. typedef enum { SDIO_SLAVE_LL_SLVINT_0 = BIT(0), ///< General purpose interrupt bit 0. diff --git a/components/esp_hal_sd/esp32c6/include/hal/sdio_slave_ll.h b/components/esp_hal_sd/esp32c6/include/hal/sdio_slave_ll.h index 1276518a899..c48ec070f7d 100644 --- a/components/esp_hal_sd/esp32c6/include/hal/sdio_slave_ll.h +++ b/components/esp_hal_sd/esp32c6/include/hal/sdio_slave_ll.h @@ -67,6 +67,9 @@ typedef struct sdio_slave_ll_desc_s { }; } sdio_slave_ll_desc_t; +/* Maximum buffer size that a single SDIO slave DMA descriptor can point to. */ +#define SDIO_SLAVE_LL_DMA_DESC_MAX_BUF_SIZE ((1 << 14) - 1) + /// Mask of general purpose interrupts sending from the host. typedef enum { SDIO_SLAVE_LL_SLVINT_0 = BIT(0), ///< General purpose interrupt bit 0. diff --git a/components/esp_hal_sd/esp32c61/include/hal/sdio_slave_ll.h b/components/esp_hal_sd/esp32c61/include/hal/sdio_slave_ll.h index f3d045f1e1e..edb4fbf467c 100644 --- a/components/esp_hal_sd/esp32c61/include/hal/sdio_slave_ll.h +++ b/components/esp_hal_sd/esp32c61/include/hal/sdio_slave_ll.h @@ -67,6 +67,9 @@ typedef struct sdio_slave_ll_desc_s { }; } sdio_slave_ll_desc_t; +/* Maximum buffer size that a single SDIO slave DMA descriptor can point to. */ +#define SDIO_SLAVE_LL_DMA_DESC_MAX_BUF_SIZE ((1 << 14) - 1) + /// Mask of general purpose interrupts sending from the host. typedef enum { SDIO_SLAVE_LL_SLVINT_0 = BIT(0), ///< General purpose interrupt bit 0. diff --git a/docs/en/api-reference/peripherals/sdio_slave.rst b/docs/en/api-reference/peripherals/sdio_slave.rst index 5bfb851ce8e..9af41af428f 100644 --- a/docs/en/api-reference/peripherals/sdio_slave.rst +++ b/docs/en/api-reference/peripherals/sdio_slave.rst @@ -223,7 +223,7 @@ Each time the slave has data to send, it raises an interrupt, and the host reque To avoid overhead from copying data, the driver itself does not have any buffer inside. Namely, the DMA takes data directly from the buffer provided by the application. The application should not touch the buffer until the sending is finished, so as to ensure that the data is transferred correctly. -The sending mode can be set in the ``sending_mode`` member of ``sdio_slave_config_t``, and the buffer numbers can be set in the ``send_queue_size``. All the buffers are restricted to be no larger than 4092 bytes. Though in the stream mode, several buffers can be sent in one transfer, each buffer is still counted as one in the queue. +The sending mode can be set in the ``sending_mode`` member of ``sdio_slave_config_t``, and the buffer numbers can be set in the ``send_queue_size``. Each buffer is restricted by the maximum size supported by a single SDIO slave DMA descriptor, which is chip-dependent. Though in the stream mode, several buffers can be sent in one transfer, each buffer is still counted as one in the queue. The application can call ``sdio_slave_transmit`` to send packets. In this case, the function returns when the transfer is successfully done, so the queue is not fully used. When higher efficiency is required, the application can use the following functions instead: diff --git a/docs/zh_CN/api-reference/peripherals/sdio_slave.rst b/docs/zh_CN/api-reference/peripherals/sdio_slave.rst index 91f50ccceda..bfbd53b4907 100644 --- a/docs/zh_CN/api-reference/peripherals/sdio_slave.rst +++ b/docs/zh_CN/api-reference/peripherals/sdio_slave.rst @@ -223,7 +223,7 @@ SDIO 从机驱动程序的相关术语如下: 为减少复制数据的开销,驱动程序本身没有内部缓冲区,DMA 直接从应用程序提供的缓冲区中获取数据。发送完成前,应用程序不应该访问缓冲区,以确保数据传输的正确性。 -结构体 ``sdio_slave_config_t`` 中的 ``sending_mode`` 可以设置发送模式,``send_queue_size`` 可以设置缓冲区数量。缓冲区大小均限制在 4092 字节内。尽管在流模式下,一次传输可以发送多个缓冲区,但每个缓冲区在队列中仍然计为一个。 +结构体 ``sdio_slave_config_t`` 中的 ``sending_mode`` 可以设置发送模式,``send_queue_size`` 可以设置缓冲区数量。每个缓冲区的大小都受单个 SDIO slave DMA 描述符可支持的最大长度限制,且该限制因芯片而异。尽管在流模式下,一次传输可以发送多个缓冲区,但每个缓冲区在队列中仍然计为一个。 应用程序可以调用 ``sdio_slave_transmit`` 函数发送数据包。此时,函数在传输完成后返回,因此队列并未完全占用。若需要更高效率,应用程序可以改用以下函数: