Merge branch 'feat/sdio_slave_eof' into 'master'

sdio_slave: allow getting end of frame information

See merge request espressif/esp-idf!13510
This commit is contained in:
Michael (XIAO Xufeng) 2021-06-20 16:31:54 +00:00
commit 67743ac444
6 changed files with 241 additions and 105 deletions

View File

@ -4,8 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _DRIVER_SDIO_SLAVE_H_
#define _DRIVER_SDIO_SLAVE_H_
#pragma once
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
@ -132,6 +131,28 @@ esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle);
*/
esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle);
/** Get buffer of received data if exist with packet information. The driver returns the ownership of the buffer to the app.
*
* When you see return value is ``ESP_ERR_NOT_FINISHED``, you should call this API iteratively until the return value is ``ESP_OK``.
* All the continuous buffers returned with ``ESP_ERR_NOT_FINISHED``, together with the last buffer returned with ``ESP_OK``, belong to one packet from the host.
*
* You can call simpler ``sdio_slave_recv`` instead, if the host never send data longer than the Receiving buffer size,
* or you don't care about the packet boundary (e.g. the data is only a byte stream).
*
* @param handle_ret Handle of the buffer holding received data. Use this handle in ``sdio_slave_recv_load_buf()`` to receive in the same buffer again.
* @param wait Time to wait before data received.
*
* @note Call ``sdio_slave_load_buf`` with the handle to re-load the buffer onto the link list, and receive with the same buffer again.
* The address and length of the buffer got here is the same as got from `sdio_slave_get_buffer`.
*
* @return
* - ESP_ERR_INVALID_ARG if handle_ret is NULL
* - ESP_ERR_TIMEOUT if timeout before receiving new data
* - ESP_ERR_NOT_FINISHED if returned buffer is not the end of a packet from the host, should call this API again until the end of a packet
* - ESP_OK if success
*/
esp_err_t sdio_slave_recv_packet(sdio_slave_buf_handle_t* handle_ret, TickType_t wait);
/** Get received data if exist. The driver returns the ownership of the buffer to the app.
*
* @param handle_ret Handle to the buffer holding received data. Use this handle in ``sdio_slave_recv_load_buf`` to receive in the same buffer again.
@ -265,5 +286,3 @@ esp_err_t sdio_slave_wait_int(int pos, TickType_t wait);
#ifdef __cplusplus
}
#endif
#endif /*_DRIVER_SDIO_SLAVE_H */

View File

@ -207,7 +207,9 @@ static inline void deinit_context(void)
vQueueDelete(context.ret_queue);
context.ret_queue = NULL;
}
if (context.remain_cnt != NULL) vSemaphoreDelete(context.remain_cnt);
if (context.remain_cnt != NULL) {
vSemaphoreDelete(context.remain_cnt);
}
free(context.hal->send_desc_queue.data);
context.hal->send_desc_queue.data = NULL;
free(context.hal);
@ -222,7 +224,9 @@ static esp_err_t init_context(const sdio_slave_config_t *config)
//initialize and configure the HAL
context.hal = (sdio_slave_context_t *)heap_caps_calloc(sizeof(sdio_slave_context_t), 1, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (context.hal == NULL) goto no_mem;
if (context.hal == NULL) {
goto no_mem;
}
context.hal->sending_mode = config->sending_mode;
context.hal->timing = config->timing;
@ -233,7 +237,9 @@ static esp_err_t init_context(const sdio_slave_config_t *config)
//one item is not used.
buf->size = SDIO_SLAVE_SEND_DESC_SIZE * (config->send_queue_size + 1);
buf->data = (uint8_t *)heap_caps_malloc(buf->size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (buf->data == NULL) goto no_mem;
if (buf->data == NULL) {
goto no_mem;
}
sdio_slave_hal_init(context.hal);
@ -251,10 +257,14 @@ static esp_err_t init_context(const sdio_slave_config_t *config)
}
context.remain_cnt = xSemaphoreCreateCounting(context.config.send_queue_size, context.config.send_queue_size);
if (context.remain_cnt == NULL) goto no_mem;
if (context.remain_cnt == NULL) {
goto no_mem;
}
context.ret_queue = xQueueCreate(config->send_queue_size, sizeof(void *));
if (context.ret_queue == NULL) goto no_mem;
if (context.ret_queue == NULL) {
goto no_mem;
}
return ESP_OK;
@ -335,14 +345,20 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
intr_handle_t intr_handle = NULL;
const int flags = 0;
r = esp_intr_alloc(ETS_SLC0_INTR_SOURCE, flags, sdio_intr, NULL, &intr_handle);
if (r != ESP_OK) return r;
if (r != ESP_OK) {
return r;
}
r = init_context(config);
if (r != ESP_OK) return r;
if (r != ESP_OK) {
return r;
}
context.intr_handle = intr_handle;
r = sdio_slave_hw_init(config);
if (r != ESP_OK) return r;
if (r != ESP_OK) {
return r;
}
sdio_slave_reset();
return ESP_OK;
@ -362,7 +378,9 @@ void sdio_slave_deinit(void)
//unregister all buffers that is loaded and not returned
while (1) {
desc = (recv_desc_t *)sdio_slave_hal_recv_unload_desc(context.hal);
if (desc == NULL) break;
if (desc == NULL) {
break;
}
free(desc);
}
esp_err_t ret = esp_intr_free(context.intr_handle);
@ -378,13 +396,17 @@ esp_err_t sdio_slave_start(void)
sdio_slave_hostint_t intr = (sdio_slave_hostint_t)UINT32_MAX;
sdio_slave_hal_hostint_clear(context.hal, &intr);
ret = sdio_slave_hal_send_start(context.hal);
if (ret != ESP_OK) return ret;
if (ret != ESP_OK) {
return ret;
}
critical_enter_recv();
sdio_slave_hal_recv_start(context.hal);
critical_exit_recv();
ret = ESP_OK;
if (ret != ESP_OK) return ret;
if (ret != ESP_OK) {
return ret;
}
sdio_slave_hal_set_ioready(context.hal, true);
return ESP_OK;
@ -439,11 +461,15 @@ static void sdio_intr_host(void* arg)
portBASE_TYPE yield = pdFALSE;
for (int i = 0; i < 8; i++) {
if (BIT(i) & int_val) {
if (context.config.event_cb != NULL) (*context.config.event_cb)(i);
if (context.config.event_cb != NULL) {
(*context.config.event_cb)(i);
}
xSemaphoreGiveFromISR(context.events[i], &yield);
}
}
if (yield) portYIELD_FROM_ISR();
if (yield) {
portYIELD_FROM_ISR();
}
}
esp_err_t sdio_slave_wait_int(int pos, TickType_t wait)
@ -454,8 +480,12 @@ esp_err_t sdio_slave_wait_int(int pos, TickType_t wait)
uint8_t sdio_slave_read_reg(int pos)
{
if (pos >= 28 && pos <= 31) SDIO_SLAVE_LOGW("%s: interrupt reg, for reference", __FUNCTION__);
if (pos < 0 || pos >= 64) SDIO_SLAVE_LOGE("read register address wrong");
if (pos >= 28 && pos <= 31) {
SDIO_SLAVE_LOGW("%s: interrupt reg, for reference", __FUNCTION__);
}
if (pos < 0 || pos >= 64) {
SDIO_SLAVE_LOGE("read register address wrong");
}
return sdio_slave_hal_host_get_reg(context.hal, pos);
}
@ -549,7 +579,9 @@ static void sdio_intr_send(void* arg)
sdio_slave_hal_send_new_packet_if_exist(context.hal);
if (yield) portYIELD_FROM_ISR();
if (yield) {
portYIELD_FROM_ISR();
}
}
esp_err_t sdio_slave_send_queue(uint8_t *addr, size_t len, void *arg, TickType_t wait)
@ -559,12 +591,16 @@ esp_err_t sdio_slave_send_queue(uint8_t* addr, size_t len, void* arg, TickType_t
ESP_ERR_INVALID_ARG);
portBASE_TYPE cnt_ret = xSemaphoreTake(context.remain_cnt, wait);
if (cnt_ret != pdTRUE) return ESP_ERR_TIMEOUT;
if (cnt_ret != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
portENTER_CRITICAL(&context.write_spinlock);
esp_err_t ret = sdio_slave_hal_send_queue(context.hal, addr, len, arg);
portEXIT_CRITICAL(&context.write_spinlock);
if (ret != ESP_OK) return ret;
if (ret != ESP_OK) {
return ret;
}
return ESP_OK;
}
@ -573,8 +609,12 @@ esp_err_t sdio_slave_send_get_finished(void** out_arg, TickType_t wait)
{
void *arg = NULL;
portBASE_TYPE err = xQueueReceive(context.ret_queue, &arg, wait);
if (out_arg) *out_arg = arg;
if (err != pdTRUE) return ESP_ERR_TIMEOUT;
if (out_arg) {
*out_arg = arg;
}
if (err != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
}
@ -584,9 +624,13 @@ esp_err_t sdio_slave_transmit(uint8_t* addr, size_t len)
uint32_t ret_stamp;
esp_err_t err = sdio_slave_send_queue(addr, len, (void *)timestamp, portMAX_DELAY);
if (err != ESP_OK) return err;
if (err != ESP_OK) {
return err;
}
err = sdio_slave_send_get_finished((void **)&ret_stamp, portMAX_DELAY);
if (err != ESP_OK) return err;
if (err != ESP_OK) {
return err;
}
SDIO_SLAVE_CHECK(ret_stamp == timestamp, "already sent without return before", ESP_ERR_INVALID_STATE);
return ESP_OK;
@ -644,7 +688,9 @@ static esp_err_t recv_flush_data(void)
{
while (1) {
portBASE_TYPE ret = xSemaphoreTake(context.recv_event, 0);
if (ret == pdFALSE) break;
if (ret == pdFALSE) {
break;
}
critical_enter_recv();
sdio_slave_hal_recv_flush_one_buffer(context.hal);
critical_exit_recv();
@ -667,7 +713,9 @@ static void sdio_intr_recv(void* arg)
// if no more items on the list, go back and check again the interrupt,
// will loop until the interrupt bit is kept cleared.
}
if (yield) portYIELD_FROM_ISR();
if (yield) {
portYIELD_FROM_ISR();
}
}
esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle)
@ -704,10 +752,32 @@ sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)
esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t *handle_ret, uint8_t **out_addr, size_t *out_len, TickType_t wait)
{
SDIO_SLAVE_CHECK(handle_ret != NULL, "handle address cannot be 0", ESP_ERR_INVALID_ARG);
portBASE_TYPE ret = xSemaphoreTake(context.recv_event, wait);
if (ret == pdFALSE) return ESP_ERR_TIMEOUT;
esp_err_t ret = sdio_slave_recv_packet(handle_ret, wait);
if (ret == ESP_ERR_NOT_FINISHED) {
//This API was not awared of the EOF info, return ESP_OK to keep back-compatible.
ret = ESP_OK;
}
if (ret == ESP_OK) {
recv_desc_t *desc = (recv_desc_t *)(*handle_ret);
if (out_addr) {
*out_addr = (uint8_t *)desc->hal_desc.buf;
}
if (out_len) {
*out_len = desc->hal_desc.length;
}
}
return ret;
}
esp_err_t sdio_slave_recv_packet(sdio_slave_buf_handle_t *handle_ret, TickType_t wait)
{
SDIO_SLAVE_CHECK(handle_ret != NULL, "handle address cannot be 0", ESP_ERR_INVALID_ARG);
portBASE_TYPE err = xSemaphoreTake(context.recv_event, wait);
if (err == pdFALSE) {
return ESP_ERR_TIMEOUT;
}
esp_err_t ret = ESP_OK;
critical_enter_recv();
//remove from queue, add back to reg list.
recv_desc_t *desc = (recv_desc_t *)sdio_slave_hal_recv_unload_desc(context.hal);
@ -716,9 +786,11 @@ esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t* handle_ret, uint8_t **out_add
critical_exit_recv();
*handle_ret = (sdio_slave_buf_handle_t)desc;
if (out_addr) *out_addr = (uint8_t*)desc->hal_desc.buf;
if (out_len) *out_len = desc->hal_desc.length;
return ESP_OK;
if (!desc->hal_desc.eof) {
ret = ESP_ERR_NOT_FINISHED;
}
return ret;
}
esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)
@ -735,9 +807,13 @@ esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)
uint8_t *sdio_slave_recv_get_buf(sdio_slave_buf_handle_t handle, size_t *len_o)
{
if (handle == NULL) return NULL;
if (handle == NULL) {
return NULL;
}
recv_desc_t *desc = (recv_desc_t *)handle;
if (len_o!= NULL) *len_o= desc->hal_desc.length;
if (len_o != NULL) {
*len_o = desc->hal_desc.length;
}
return (uint8_t *)desc->hal_desc.buf;
}

View File

@ -39,6 +39,8 @@ typedef int esp_err_t;
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
#define ESP_ERR_INVALID_MAC 0x10B /*!< MAC address was invalid */
#define ESP_ERR_NOT_FINISHED 0x10C /*!< There are items remained to retrieve */
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */

View File

@ -20,8 +20,6 @@
#include "freertos/semphr.h"
#define ESP_ERR_NOT_FINISHED 0x201 ///< There is still remaining data.
struct essl_dev_t;
/// Handle of an ESSL device
typedef struct essl_dev_t* essl_handle_t;

View File

@ -187,9 +187,14 @@ the buffer number of slave.
To allow the host sending data to the slave, the application has to load buffers to the slave driver by the following steps:
1. Register the buffer by calling ``sdio_slave_recv_register_buf``, and get the handle of the registered buffer. The driver
will allocate memory for the linked-list descriptor needed to link the buffer onto the hardware.
will allocate memory for the linked-list descriptor needed to link the buffer onto the hardware. The size of these buffers should equal to the Receiving buffer size.
2. Load buffers onto the driver by passing the buffer handle to ``sdio_slave_recv_load_buf``.
3. Call ``sdio_slave_recv`` to get the received data. If non-blocking call is needed, set ``wait=0``.
3. Get the received data by calling ``sdio_slave_recv`` or ``sdio_slave_recv_packet``. If non-blocking call is needed, set ``wait=0``.
The difference between two APIs is that, ``sdio_slave_recv_packet`` gives more information about packet, which can consist of several buffers. When ``ESP_ERR_NOT_FINISHED`` is returned by this API, you should call this API iteratively until the return value is ``ESP_OK``. All the continuous buffers returned with ``ESP_ERR_NOT_FINISHED``, together with the last buffer returned with ``ESP_OK``, belong to one packet from the host. Call ``sdio_slave_recv_get_buf`` to get the address of the received data, and the actual length received in each buffer. The packet length is the sum of received length of all the buffers in the packet.
If the host never send data longer than the Receiving buffer size, or you don't care about the packet boundary (e.g. the data is only a byte stream), you can call the simpler version ``sdio_slave_recv`` instead.
4. Pass the handle of processed buffer back to the driver by ``sdio_recv_load_buf`` again.
.. note:: To avoid overhead from copying data, the driver itself doesn't have any buffer inside, the application is

View File

@ -186,9 +186,8 @@ void app_main(void)
sdio_slave_write_reg(0, JOB_IDLE);
sdio_slave_buf_handle_t handle;
for(int i = 0; i < BUFFER_NUM; i++) {
handle = sdio_slave_recv_register_buf(buffer[i]);
sdio_slave_buf_handle_t handle = sdio_slave_recv_register_buf(buffer[i]);
assert(handle != NULL);
ret = sdio_slave_recv_load_buf(handle);
@ -211,22 +210,53 @@ void app_main(void)
ESP_LOGI(TAG, EV_STR("slave ready"));
for(;;) {
const TickType_t non_blocking = 0, blocking = portMAX_DELAY;
sdio_slave_buf_handle_t recv_queue[BUFFER_NUM];
int packet_size = 0;
sdio_slave_buf_handle_t handle;
ret = sdio_slave_recv_packet(&handle, non_blocking);
if (ret != ESP_ERR_TIMEOUT) {
recv_queue[packet_size++] = handle;
//Receive following buffers in the same packet in blocking mode.
//You can also skip this step and handle the data buffer by buffer, if the data is a stream or you don't care about the packet boundary.
while (ret == ESP_ERR_NOT_FINISHED) {
//The return value must be ESP_OK or ESP_ERR_NOT_FINISHED.
ret = sdio_slave_recv_packet(&handle, blocking);
recv_queue[packet_size++] = handle;
}
ESP_ERROR_CHECK(ret);
int packet_len = 0;
for (int i = 0; i < packet_size; i++) {
size_t buf_len;
sdio_slave_recv_get_buf(recv_queue[i], &buf_len);
packet_len += buf_len;
}
ESP_LOGI(TAG, "Packet received, len: %d", packet_len);
for (int i = 0; i < packet_size; i++) {
handle = recv_queue[i];
//handle data in the buffer, here we print them and send the same buffer back to the host
//receive data and send back to host.
size_t length;
uint8_t *ptr;
uint8_t *ptr = sdio_slave_recv_get_buf(handle, &length);
const TickType_t non_blocking = 0;
ret = sdio_slave_recv(&handle, &ptr, &length, non_blocking);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "handle: %p, recv len: %d, data:", handle, length);
ESP_LOGI(TAG, "Buffer %d, len: %d", i, length);
ESP_LOG_BUFFER_HEXDUMP(TAG, ptr, length, ESP_LOG_INFO);
/* If buffer is no longer used, call sdio_slave_recv_load_buf to return it here. Since we wants to show how
* to share large buffers between drivers here (we share between sending and receiving), keep the buffer
* until the buffer is sent by sending driver.
/* If buffer is no longer used, we can call sdio_slave_recv_load_buf to use it to receive data again.
* But here we wants to show how to share large buffers between drivers here (we share the buffer
* between sending and receiving), the buffer is kept until the buffer is sent by sending driver.
*/
//send the received buffer to host, with the handle as the argument
ret = sdio_slave_send_queue(ptr, length, handle, non_blocking);
//the recv_buf_handle is used as the argument, so that we can easily load the same buffer to recv driver,
// after it's sent
void* send_args = handle;
ret = sdio_slave_send_queue(ptr, length, send_args, non_blocking);
if (ret == ESP_ERR_TIMEOUT) {
// send failed, direct return the buffer to rx
ESP_LOGE(TAG, "send_queue full, discard received.");
@ -234,12 +264,18 @@ void app_main(void)
}
ESP_ERROR_CHECK(ret);
}
}
// if there's finished sending desc, return the buffer to receiving driver
for(;;){
sdio_slave_buf_handle_t handle;
ret = sdio_slave_send_get_finished(&handle, 0);
if (ret == ESP_ERR_TIMEOUT) break;
void* send_args = NULL;
ret = sdio_slave_send_get_finished(&send_args, 0);
//extract the buffer handle from the sending args
sdio_slave_buf_handle_t handle = (sdio_slave_buf_handle_t)send_args;
if (ret == ESP_ERR_TIMEOUT) {
break;
}
ESP_ERROR_CHECK(ret);
ret = sdio_slave_recv_load_buf(handle);
ESP_ERROR_CHECK(ret);