sdio_slave: allow getting end of frame information

This commit is contained in:
Michael (XIAO Xufeng) 2021-05-10 23:24:20 +08:00
parent 38f0d52e2c
commit 591e4c4b31
6 changed files with 115 additions and 37 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

@ -704,10 +704,26 @@ 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 +732,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)

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);