mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(driver_spi): fixed spi_dma_malloc() utils api crash
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user