feat(driver_spi): slave driver support config different tx/rx length

This commit is contained in:
wanckl
2026-07-06 11:58:20 +08:00
committed by Wan Lei
parent 169aded5b1
commit 072f62d70d
7 changed files with 103 additions and 66 deletions
@@ -67,7 +67,9 @@ typedef struct {
*/
struct spi_slave_transaction_t {
uint32_t flags; ///< Bitwise OR of SPI_SLAVE_TRANS_* flags
size_t length; ///< Total data length, in bits
size_t length; ///< Total data length, in bits. It both for TX and RX, do NOT use together with independent length
size_t tx_length; ///< Independent Tx data length, in bits, using with tx_buffer. do NOT use together with `length`
size_t rx_length; ///< Independent Rx data length, in bits, using with rx_buffer. do NOT use together with `length`
size_t trans_len; ///< Transaction data length, in bits
const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase
void *rx_buffer; /**< Pointer to receive buffer, or NULL for no MISO phase.
+23 -10
View File
@@ -6,6 +6,7 @@
#include <string.h>
#include <stdatomic.h>
#include <sys/param.h>
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_check.h"
@@ -413,7 +414,8 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
free(priv_trans->tx_buffer);
}
if (trans->rx_buffer && (trans->rx_buffer != priv_trans->rx_buffer)) {
memcpy(trans->rx_buffer, priv_trans->rx_buffer, (trans->length + 7) / 8);
size_t compatible_len = trans->rx_length ? trans->rx_length : trans->length;
memcpy(trans->rx_buffer, priv_trans->rx_buffer, (MIN(compatible_len, trans->trans_len) + 7) / 8);
free(priv_trans->rx_buffer);
}
}
@@ -429,12 +431,12 @@ static esp_err_t SPI_SLAVE_ATTR spi_slave_setup_priv_trans(spi_host_device_t hos
}
bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO);
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, (trans->length + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer);
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, ((trans->length ? trans->length : trans->tx_length) + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer);
if (ret != ESP_OK) {
spi_slave_uninstall_priv_trans(host, priv_trans);
return ret;
}
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, (trans->length + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer);
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, ((trans->length ? trans->length : trans->rx_length) + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer);
if (ret != ESP_OK) {
spi_slave_uninstall_priv_trans(host, priv_trans);
}
@@ -446,7 +448,12 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi
BaseType_t r;
SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG);
SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(!(trans_desc->length && (trans_desc->tx_length || trans_desc->rx_length)), "length and tx_length/rx_length are mutually exclusive", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "tx data transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->tx_length <= spihost[host]->bus_attr->max_transfer_sz * 8, "tx data transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->rx_length <= spihost[host]->bus_attr->max_transfer_sz * 8, "rx data transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(!trans_desc->tx_buffer || (trans_desc->tx_length || trans_desc->length), "set tx_buffer but no length or tx_length", ESP_ERR_INVALID_ARG);
SPI_CHECK(!trans_desc->rx_buffer || (trans_desc->rx_length || trans_desc->length), "set rx_buffer but no length or rx_length", ESP_ERR_INVALID_ARG);
spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc};
SPI_CHECK(ESP_OK == spi_slave_setup_priv_trans(host, &priv_trans), "slave setup priv_trans failed", ESP_ERR_NO_MEM);
@@ -498,7 +505,12 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c
BaseType_t do_yield = pdFALSE;
ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host");
ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave");
ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum");
ESP_RETURN_ON_FALSE_ISR(!(trans_desc->length && (trans_desc->tx_length || trans_desc->rx_length)), ESP_ERR_INVALID_ARG, SPI_TAG, "length and tx_length/rx_length are mutually exclusive");
ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "tx data transfer > host maximum");
ESP_RETURN_ON_FALSE_ISR(trans_desc->tx_length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "tx data transfer > host maximum");
ESP_RETURN_ON_FALSE_ISR(trans_desc->rx_length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "rx data transfer > host maximum");
ESP_RETURN_ON_FALSE_ISR(!trans_desc->tx_buffer || (trans_desc->tx_length || trans_desc->length), ESP_ERR_INVALID_ARG, SPI_TAG, "set tx_buffer but no length or tx_length");
ESP_RETURN_ON_FALSE_ISR(!trans_desc->rx_buffer || (trans_desc->rx_length || trans_desc->length), ESP_ERR_INVALID_ARG, SPI_TAG, "set rx_buffer but no length or rx_length");
spi_slave_trans_priv_t priv_trans = {
.trans = (spi_slave_transaction_t *)trans_desc,
@@ -507,8 +519,8 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c
};
if (spihost[host]->bus_attr->dma_enabled) {
// isr api is not allowed to auto_malloc, so don't need to 'uninstall' anything here, return directly
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->tx_buffer, (trans_desc->length + 7) / 8, true, true, false, &priv_trans.tx_buffer), SPI_TAG, "");
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->rx_buffer, (trans_desc->length + 7) / 8, false, true, false, &priv_trans.rx_buffer), SPI_TAG, "");
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->tx_buffer, ((trans_desc->length ? trans_desc->length : trans_desc->tx_length) + 7) / 8, true, true, false, &priv_trans.tx_buffer), SPI_TAG, "");
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->rx_buffer, ((trans_desc->length ? trans_desc->length : trans_desc->rx_length) + 7) / 8, false, true, false, &priv_trans.rx_buffer), SPI_TAG, "");
}
r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield);
if (!r) {
@@ -589,14 +601,14 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_tr
static void SPI_SLAVE_ISR_ATTR s_spi_slave_dma_prepare_data(spi_dma_ctx_t *dma_ctx, spi_slave_hal_context_t *hal)
{
if (hal->rx_buffer) {
spicommon_dma_desc_setup_link(dma_ctx->dmadesc_rx, hal->rx_buffer, ((hal->bitlen + 7) / 8), true);
spicommon_dma_desc_setup_link(dma_ctx->dmadesc_rx, hal->rx_buffer, (hal->rx_bitlen + 7) / 8, true);
spi_dma_reset(dma_ctx->rx_dma_chan);
spi_slave_hal_hw_prepare_rx(hal->hw);
spi_dma_start(dma_ctx->rx_dma_chan, dma_ctx->dmadesc_rx);
}
if (hal->tx_buffer) {
spicommon_dma_desc_setup_link(dma_ctx->dmadesc_tx, hal->tx_buffer, (hal->bitlen + 7) / 8, false);
spicommon_dma_desc_setup_link(dma_ctx->dmadesc_tx, hal->tx_buffer, (hal->tx_bitlen + 7) / 8, false);
spi_dma_reset(dma_ctx->tx_dma_chan);
spi_slave_hal_hw_prepare_tx(hal->hw);
@@ -718,7 +730,8 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
//We have a transaction. Send it.
host->cur_trans = priv_trans;
hal->bitlen = priv_trans.trans->length;
hal->tx_bitlen = priv_trans.trans->tx_length ? priv_trans.trans->tx_length : priv_trans.trans->length;
hal->rx_bitlen = priv_trans.trans->rx_length ? priv_trans.trans->rx_length : priv_trans.trans->length;
hal->rx_buffer = priv_trans.rx_buffer;
hal->tx_buffer = priv_trans.tx_buffer;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -152,7 +152,7 @@ TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
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_length = 8 * 32;
slave_t.tx_buffer = slave_txbuf;
slave_t.rx_buffer = NULL;
slave_t.flags |= SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
@@ -189,7 +189,7 @@ TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
}
#define TEST_SLV_RX_BUF_LEN 15
TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]")
TEST_CASE("Test slave rx_buffer overwrite if trans_len below/over config_len", "[spi]")
{
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
@@ -198,65 +198,79 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spidev0));
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
for (int use_dma = 0; use_dma < 2; use_dma++) {
printf("\n------------- DMA: %s -------------\n", use_dma ? "Enable" : "Disable");
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, use_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED));
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
//initialize master and slave on the same pins break some of the output configs, fix them
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
uint8_t master_tx[TEST_SLV_RX_BUF_LEN], master_rx[TEST_SLV_RX_BUF_LEN];
uint8_t slave_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN];
for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) {
master_tx[i] = TEST_SLV_RX_BUF_LEN - i;
slave_tx[i] = i + 1;
slave_rx[i] = 100;
}
uint8_t master_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN];
for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) {
master_tx[i] = TEST_SLV_RX_BUF_LEN - i;
slave_rx[i] = 100;
}
//------------------------------ trans_len < config_len ------------------------------
printf("Testing trans_len < config_len:\n");
spi_slave_transaction_t *slave_out, slave_tans = {
.tx_buffer = slave_tx,
.rx_buffer = slave_rx,
.tx_length = 8 * 10, // let tx shorter than rx but larger than trans_len
.rx_length = 8 * (TEST_SLV_RX_BUF_LEN),
.flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO,
};
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_tans, portMAX_DELAY));
//------------------------------ trans_len < config_len ------------------------------
printf("Testing trans_len < config_len:\n");
spi_slave_transaction_t *slave_out, slave_tans = {
.length = 8 * TEST_SLV_RX_BUF_LEN,
.rx_buffer = slave_rx,
};
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_tans, portMAX_DELAY));
spi_transaction_t master_tans = {
.length = 8 * 7,
.rx_buffer = master_rx,
.tx_buffer = master_tx,
};
memset(master_rx, 0x55, TEST_SLV_RX_BUF_LEN);
spi_device_polling_transmit(spidev0, &master_tans);
spi_transaction_t master_tans = {
.length = 8 * 7,
.tx_buffer = master_tx,
};
spi_device_polling_transmit(spidev0, &master_tans);
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &slave_out, portMAX_DELAY));
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &slave_out, portMAX_DELAY));
ESP_LOGI(SLAVE_TAG, "trans_len: %d, config_len rx %d tx %d", master_tans.length / 8, slave_tans.rx_length / 8, slave_tans.tx_length / 8);
ESP_LOG_BUFFER_HEX("master tx", master_tans.tx_buffer, master_tans.length / 8);
ESP_LOG_BUFFER_HEX("slave rx", slave_tans.rx_buffer, TEST_SLV_RX_BUF_LEN);
ESP_LOG_BUFFER_HEX("master rx", master_rx, TEST_SLV_RX_BUF_LEN);
ESP_LOGI(SLAVE_TAG, "trans_len: %d, config_len %d", slave_tans.trans_len / 8, slave_tans.length / 8);
ESP_LOG_BUFFER_HEX("master tx", master_tans.tx_buffer, master_tans.length / 8);
ESP_LOG_BUFFER_HEX("slave rx", slave_tans.rx_buffer, TEST_SLV_RX_BUF_LEN);
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
for (uint8_t i = slave_tans.trans_len; i < slave_tans.rx_length; i += 8) {
TEST_ASSERT_EQUAL(slave_rx[i / 8], 100);
}
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tx, master_rx, master_tans.length / 8);
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
for (uint8_t i = slave_tans.trans_len; i < slave_tans.length; i += 8) {
TEST_ASSERT_EQUAL(slave_rx[i / 8], 100);
}
//------------------------------ trans_len > config_len ------------------------------
printf("Testing trans_len > config_len:\n");
slave_tans.rx_length = 8 * 8;
slave_tans.tx_length = 8 * 10;
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_tans, portMAX_DELAY));
//------------------------------ trans_len > config_len ------------------------------
printf("Testing trans_len > config_len:\n");
slave_tans.length = 8 * 9;
TEST_ESP_OK(spi_slave_queue_trans(TEST_SLAVE_HOST, &slave_tans, portMAX_DELAY));
master_tans.length = 8 * 13,
master_tans.rxlength = 8 * 13;
spi_device_polling_transmit(spidev0, &master_tans);
master_tans.length = 8 * 11,
spi_device_polling_transmit(spidev0, &master_tans);
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &slave_out, portMAX_DELAY));
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SLAVE_HOST, &slave_out, portMAX_DELAY));
ESP_LOGI(SLAVE_TAG, "trans_len: %d, config_len %d", master_tans.length / 8, slave_tans.trans_len / 8);
ESP_LOG_BUFFER_HEX("master tx", master_tans.tx_buffer, master_tans.length / 8);
ESP_LOG_BUFFER_HEX("slave rx", slave_tans.rx_buffer, TEST_SLV_RX_BUF_LEN);
ESP_LOGI(SLAVE_TAG, "trans_len: %d, config_len rx %d tx %d", master_tans.length / 8, slave_tans.rx_length / 8, slave_tans.tx_length / 8);
ESP_LOG_BUFFER_HEX("master tx", master_tans.tx_buffer, master_tans.length / 8);
ESP_LOG_BUFFER_HEX("slave rx", slave_tans.rx_buffer, TEST_SLV_RX_BUF_LEN);
ESP_LOG_BUFFER_HEX("master rx", master_rx, TEST_SLV_RX_BUF_LEN);
#if !CONFIG_IDF_TARGET_ESP32 // esp32 already hardware limited trans_len <= config_len
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
#endif
for (uint8_t i = slave_tans.length; i < TEST_SLV_RX_BUF_LEN * 8; i += 8) {
TEST_ASSERT_EQUAL(slave_rx[i / 8], 100);
}
for (uint8_t i = slave_tans.rx_length; i < TEST_SLV_RX_BUF_LEN * 8; i += 8) {
TEST_ASSERT_EQUAL(slave_rx[i / 8], 100);
}
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tx, master_rx, slave_tans.tx_length / 8);
TEST_ESP_OK(spi_slave_free(TEST_SLAVE_HOST));
TEST_ESP_OK(spi_slave_free(TEST_SLAVE_HOST));
}
TEST_ESP_OK(spi_bus_remove_device(spidev0));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}