fix(driver_spi): fixed spi_dma_malloc() utils api crash

This commit is contained in:
wanckl
2025-11-20 14:47:46 +08:00
parent 263de1bb75
commit 76bd4ef138
7 changed files with 88 additions and 85 deletions
@@ -177,14 +177,15 @@ esp_err_t spi_bus_free(spi_host_device_t host_id);
/**
* @brief Helper function for malloc DMA capable memory for SPI driver
*
* @note This API will take care of the cache and hardware alignment internally.
* To free/release memory allocated by this helper function, simply calling `free()`
* @note Using this API AFTER spi_bus_initialize() is called, this API will take care of the cache and hardware
* alignment internally. To free/release memory allocated by this helper function, simply calling `free()`.
*
* @param[in] host_id SPI peripheral who will using the memory
* @param[in] size Size in bytes, the amount of memory to allocate
* @param[in] extra_heap_caps Extra heap caps based on MALLOC_CAP_DMA
*
* @return Pointer to the memory if allocated successfully
* - NULL If allocation failed or bus not initialized
*/
void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps);
@@ -54,7 +54,7 @@ typedef enum {
typedef struct {
spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
uint64_t gpio_reserve; ///< reserved output gpio bit mask
uint32_t flags; ///< Flags (attributes) of the bus
uint32_t flags; ///< Flags (SPICOMMON_BUSFLAG_* flag combination of bus abilities) of the bus
int max_transfer_sz; ///< Maximum length of bytes available to send
bool dma_enabled; ///< To enable DMA or not
size_t cache_align_int; ///< Internal memory align byte requirement
@@ -179,12 +179,11 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx);
* - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
* - ``SPICOMMON_BUSFLAG_IO4_IO7``: The bus has spi data4 ~ spi data7 connected.
* - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``.
* @param[out] io_reserved Output the reserved gpio map
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success
*/
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved);
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved);
/**
* @brief Free the IO used by a SPI peripheral
@@ -206,7 +205,6 @@ esp_err_t spicommon_bus_free_io_cfg(const spi_bus_config_t *bus_cfg, uint64_t *i
* @param cs_id Hardware CS id to route
* @param force_gpio_matrix If true, CS will always be routed through the GPIO matrix. If false,
* if the GPIO number allows it, the routing will happen through the IO_mux.
* @param[out] io_reserved Output the reserved gpio map
*/
void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_id, int force_gpio_matrix, uint64_t *io_reserved);
@@ -728,12 +728,14 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
#endif //SOC_SPI_SUPPORT_OCT
}
if (bus_ctx[host]) {
bus_ctx[host]->bus_attr.bus_cfg = *bus_config;
bus_ctx[host]->bus_attr.flags = temp_flag;
bus_ctx[host]->bus_attr.gpio_reserve = gpio_reserv;
}
if (flags_o) {
*flags_o = temp_flag;
}
if (io_reserved) {
*io_reserved |= gpio_reserv;
}
return ESP_OK;
}
@@ -777,9 +779,7 @@ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_id, i
}
gpio_func_sel(cs_io_num, PIN_FUNC_GPIO);
}
if (io_reserved) {
*io_reserved |= out_mask;
}
bus_ctx[host]->bus_attr.gpio_reserve |= out_mask;
}
void spicommon_cs_free_io(int cs_gpio_num, uint64_t *io_reserved)
@@ -828,10 +828,8 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
#endif
ESP_RETURN_ON_ERROR(spicommon_bus_alloc(host_id, "spi master"), SPI_TAG, "alloc host failed");
spi_bus_attr_t *bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id);
spicommon_bus_context_t *ctx = __containerof(bus_attr, spicommon_bus_context_t, bus_attr);
assert(bus_attr && ctx); //coverity check
bus_attr->bus_cfg = *bus_config;
spicommon_bus_context_t *ctx = bus_ctx[host_id];
spi_bus_attr_t *bus_attr = &ctx->bus_attr;
bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
@@ -872,7 +870,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
_lock_acquire(&ctx->mutex);
if (sleep_retention_module_init(spi_reg_retention_info[host_id - 1].module_id, &init_param) == ESP_OK) {
if ((bus_attr->bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) && (sleep_retention_module_allocate(spi_reg_retention_info[host_id - 1].module_id) != ESP_OK)) {
if ((bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) && (sleep_retention_module_allocate(spi_reg_retention_info[host_id - 1].module_id) != ESP_OK)) {
// even though the sleep retention create failed, SPI driver should still work, so just warning here
ESP_LOGW(SPI_TAG, "alloc sleep recover failed, peripherals may hold power on");
}
@@ -882,7 +880,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
}
_lock_release(&ctx->mutex);
#else
if (bus_attr->bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) {
if (bus_config->flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) {
ESP_LOGE(SPI_TAG, "power down peripheral in sleep is not enabled or not supported on your target");
}
#endif // SOC_SPI_SUPPORT_SLEEP_RETENTION
@@ -901,7 +899,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
}
#endif //CONFIG_PM_ENABLE
err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, &bus_attr->flags, &bus_attr->gpio_reserve);
err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL, NULL);
if (err != ESP_OK) {
goto cleanup;
}
@@ -929,10 +927,16 @@ cleanup:
void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps)
{
// As don't know the buffer will used for TX or RX, so use the max alignment requirement
size_t alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int);
SPI_CHECK(bus_ctx[host_id], "SPI %d not initialized", NULL, host_id + 1);
size_t alignment = 16;
// detailed alignment requirement is not available for slave bus, so use 16 bytes as default
if (bus_ctx[host_id]->bus_attr.flags & SPICOMMON_BUSFLAG_MASTER) {
// As don't know the buffer will used for TX or RX, so use the max alignment requirement
alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int);
}
return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA);
}
@@ -550,7 +550,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
//Set CS pin, CS options
if (dev_config->spics_io_num >= 0) {
spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio, (uint64_t *)&bus_attr->gpio_reserve);
spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio, NULL);
}
//save a pointer to device in spi_host_t
@@ -61,8 +61,7 @@ typedef struct {
typedef struct {
int id;
_Atomic spi_bus_fsm_t fsm;
uint64_t gpio_reserve;
spi_bus_config_t bus_config;
spi_bus_attr_t* bus_attr;
spi_dma_ctx_t *dma_ctx;
spi_slave_interface_config_t cfg;
intr_handle_t intr;
@@ -181,8 +180,8 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
goto cleanup;
}
memcpy(&spihost[host]->cfg, slave_config, sizeof(spi_slave_interface_config_t));
memcpy(&spihost[host]->bus_config, bus_config, sizeof(spi_bus_config_t));
spihost[host]->id = host;
spihost[host]->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host);
atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED);
spi_slave_hal_context_t *hal = &spihost[host]->hal;
@@ -213,17 +212,18 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
}
err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, &spihost[host]->flags, &spihost[host]->gpio_reserve);
err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL);
if (err != ESP_OK) {
ret = err;
goto cleanup;
}
if (slave_config->spics_io_num >= 0) {
spicommon_cs_initialize(host, slave_config->spics_io_num, 0, !bus_is_iomux(spihost[host]), &spihost[host]->gpio_reserve);
spicommon_cs_initialize(host, slave_config->spics_io_num, 0, !bus_is_iomux(spihost[host]), NULL);
// check and save where cs line really route through
spihost[host]->cs_iomux = (slave_config->spics_io_num == spi_periph_signal[host].spics0_iomux_pin) && bus_is_iomux(spihost[host]);
spihost[host]->cs_in_signal = spi_periph_signal[host].spics_in;
}
spihost[host]->flags = spihost[host]->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called
// The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line.
if (spihost[host]->dma_enabled) {
@@ -337,9 +337,9 @@ esp_err_t spi_slave_free(spi_host_device_t host)
free(spihost[host]->dma_ctx->dmadesc_rx);
spicommon_dma_chan_free(spihost[host]->dma_ctx);
}
spicommon_bus_free_io_cfg(&spihost[host]->bus_config, &spihost[host]->gpio_reserve);
spicommon_bus_free_io_cfg(&spihost[host]->bus_attr->bus_cfg, &spihost[host]->bus_attr->gpio_reserve);
if (spihost[host]->cfg.spics_io_num >= 0) {
spicommon_cs_free_io(spihost[host]->cfg.spics_io_num, &spihost[host]->gpio_reserve);
spicommon_cs_free_io(spihost[host]->cfg.spics_io_num, &spihost[host]->bus_attr->gpio_reserve);
}
esp_intr_free(spihost[host]->intr);
@@ -44,9 +44,8 @@ typedef struct {
typedef struct {
spi_host_device_t host_id;
spi_bus_config_t bus_config;
int cs_io_num;
uint64_t gpio_reserve;
spi_bus_attr_t* bus_attr;
_Atomic spi_bus_fsm_t fsm;
spi_dma_ctx_t *dma_ctx;
uint16_t internal_mem_align_size;
@@ -129,7 +128,7 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
host->int_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
host->append_mode = append_mode;
atomic_store(&host->fsm, SPI_BUS_FSM_ENABLED);
memcpy(&host->bus_config, bus_config, sizeof(spi_bus_config_t));
host->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id);
host->cs_io_num = config->spics_io_num;
ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx);
@@ -172,11 +171,12 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
host->internal_mem_align_size = 4;
#endif
ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, &host->flags, &host->gpio_reserve);
ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL);
if (ret != ESP_OK) {
goto cleanup;
}
spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), &host->gpio_reserve);
spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL);
host->flags = host->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called
spi_slave_hd_hal_config_t hal_config = {
.host_id = host_id,
@@ -349,8 +349,8 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id)
}
#endif
spicommon_bus_free_io_cfg(&host->bus_config, &host->gpio_reserve);
spicommon_cs_free_io(host->cs_io_num, &host->gpio_reserve);
spicommon_bus_free_io_cfg(&host->bus_attr->bus_cfg, &host->bus_attr->gpio_reserve);
spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve);
spicommon_bus_free(host_id);
free(host->dma_ctx->dmadesc_tx);
free(host->dma_ctx->dmadesc_rx);
@@ -130,6 +130,53 @@ TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
ESP_LOGI(SLAVE_TAG, "test passed.");
}
TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
{
custom_setup();
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
for (int i = 0; i < 4; i ++) {
//slave send
spi_slave_transaction_t slave_t;
spi_slave_transaction_t *out;
memset(&slave_t, 0, sizeof(spi_slave_transaction_t));
slave_t.length = 8 * 32;
slave_t.tx_buffer = slave_txbuf;
slave_t.rx_buffer = NULL;
slave_t.flags |= SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
// Colorize RX buffer with known pattern
memset(master_rxbuf, 0x66, sizeof(master_rxbuf));
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_t, portMAX_DELAY));
//send
spi_transaction_t t = {};
t.length = 32 * (i + 1);
if (t.length != 0) {
t.tx_buffer = NULL;
t.rx_buffer = master_rxbuf;
}
spi_device_transmit(spi, &t);
//wait for end
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &out, portMAX_DELAY));
//show result
ESP_LOGI(SLAVE_TAG, "trans_len: %d", slave_t.trans_len);
ESP_LOG_BUFFER_HEX("master rx", t.rx_buffer, t.length / 8);
ESP_LOG_BUFFER_HEX("slave tx", slave_t.tx_buffer, (slave_t.trans_len + 7) / 8);
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t.tx_buffer, t.rx_buffer, t.length / 8);
TEST_ASSERT_EQUAL(t.length, slave_t.trans_len);
}
custom_teardown();
ESP_LOGI(SLAVE_TAG, "test passed.");
}
#define TEST_SLV_RX_BUF_LEN 15
TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]")
{
@@ -202,53 +249,6 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
TEST_ESP_OK(spi_bus_remove_device(spidev0));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
{
custom_setup();
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
for (int i = 0; i < 4; i ++) {
//slave send
spi_slave_transaction_t slave_t;
spi_slave_transaction_t *out;
memset(&slave_t, 0, sizeof(spi_slave_transaction_t));
slave_t.length = 8 * 32;
slave_t.tx_buffer = slave_txbuf;
slave_t.rx_buffer = NULL;
slave_t.flags |= SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
// Colorize RX buffer with known pattern
memset(master_rxbuf, 0x66, sizeof(master_rxbuf));
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_t, portMAX_DELAY));
//send
spi_transaction_t t = {};
t.length = 32 * (i + 1);
if (t.length != 0) {
t.tx_buffer = NULL;
t.rx_buffer = master_rxbuf;
}
spi_device_transmit(spi, &t);
//wait for end
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &out, portMAX_DELAY));
//show result
ESP_LOGI(SLAVE_TAG, "trans_len: %d", slave_t.trans_len);
ESP_LOG_BUFFER_HEX("master rx", t.rx_buffer, t.length / 8);
ESP_LOG_BUFFER_HEX("slave tx", slave_t.tx_buffer, (slave_t.trans_len + 7) / 8);
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_t.tx_buffer, t.rx_buffer, t.length / 8);
TEST_ASSERT_EQUAL(t.length, slave_t.trans_len);
}
custom_teardown();
ESP_LOGI(SLAVE_TAG, "test passed.");
}
#endif // !CONFIG_SPIRAM
#endif // #if (TEST_SPI_PERIPH_NUM >= 2)