mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
commit
67743ac444
@ -4,8 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DRIVER_SDIO_SLAVE_H_
|
#pragma once
|
||||||
#define _DRIVER_SDIO_SLAVE_H_
|
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "esp_err.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);
|
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.
|
/** 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.
|
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /*_DRIVER_SDIO_SLAVE_H */
|
|
||||||
|
@ -104,7 +104,7 @@ static const char TAG[] = "sdio_slave";
|
|||||||
|
|
||||||
|
|
||||||
// sdio_slave_buf_handle_t is of type recv_desc_t*;
|
// sdio_slave_buf_handle_t is of type recv_desc_t*;
|
||||||
typedef struct recv_desc_s{
|
typedef struct recv_desc_s {
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
// the third word, pointer to next desc, is shared with the tailq entry.
|
// the third word, pointer to next desc, is shared with the tailq entry.
|
||||||
@ -163,10 +163,10 @@ typedef struct {
|
|||||||
|
|
||||||
static sdio_context_t context = CONTEXT_INIT_VAL;
|
static sdio_context_t context = CONTEXT_INIT_VAL;
|
||||||
|
|
||||||
static void sdio_intr(void*);
|
static void sdio_intr(void *);
|
||||||
static void sdio_intr_host(void*);
|
static void sdio_intr_host(void *);
|
||||||
static void sdio_intr_send(void*);
|
static void sdio_intr_send(void *);
|
||||||
static void sdio_intr_recv(void*);
|
static void sdio_intr_recv(void *);
|
||||||
|
|
||||||
static esp_err_t send_flush_data(void);
|
static esp_err_t send_flush_data(void);
|
||||||
static esp_err_t recv_flush_data(void);
|
static esp_err_t recv_flush_data(void);
|
||||||
@ -196,8 +196,8 @@ static void __attribute((unused)) dump_ll(lldesc_t *queue)
|
|||||||
|
|
||||||
static inline void deinit_context(void)
|
static inline void deinit_context(void)
|
||||||
{
|
{
|
||||||
context.config = (sdio_slave_config_t){};
|
context.config = (sdio_slave_config_t) {};
|
||||||
for(int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
if (context.events[i] != NULL) {
|
if (context.events[i] != NULL) {
|
||||||
vSemaphoreDelete(context.events[i]);
|
vSemaphoreDelete(context.events[i]);
|
||||||
context.events[i] = NULL;
|
context.events[i] = NULL;
|
||||||
@ -207,7 +207,9 @@ static inline void deinit_context(void)
|
|||||||
vQueueDelete(context.ret_queue);
|
vQueueDelete(context.ret_queue);
|
||||||
context.ret_queue = NULL;
|
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);
|
free(context.hal->send_desc_queue.data);
|
||||||
context.hal->send_desc_queue.data = NULL;
|
context.hal->send_desc_queue.data = NULL;
|
||||||
free(context.hal);
|
free(context.hal);
|
||||||
@ -216,13 +218,15 @@ static inline void deinit_context(void)
|
|||||||
|
|
||||||
static esp_err_t init_context(const sdio_slave_config_t *config)
|
static esp_err_t init_context(const sdio_slave_config_t *config)
|
||||||
{
|
{
|
||||||
SDIO_SLAVE_CHECK(*(uint32_t*)&context.config == 0, "sdio slave already initialized", ESP_ERR_INVALID_STATE);
|
SDIO_SLAVE_CHECK(*(uint32_t *)&context.config == 0, "sdio slave already initialized", ESP_ERR_INVALID_STATE);
|
||||||
context = (sdio_context_t)CONTEXT_INIT_VAL;
|
context = (sdio_context_t)CONTEXT_INIT_VAL;
|
||||||
context.config = *config;
|
context.config = *config;
|
||||||
|
|
||||||
//initialize and configure the HAL
|
//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);
|
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->sending_mode = config->sending_mode;
|
||||||
context.hal->timing = config->timing;
|
context.hal->timing = config->timing;
|
||||||
@ -231,16 +235,18 @@ static esp_err_t init_context(const sdio_slave_config_t *config)
|
|||||||
//initialize ringbuffer resources
|
//initialize ringbuffer resources
|
||||||
sdio_ringbuf_t *buf = &(context.hal->send_desc_queue);
|
sdio_ringbuf_t *buf = &(context.hal->send_desc_queue);
|
||||||
//one item is not used.
|
//one item is not used.
|
||||||
buf->size = SDIO_SLAVE_SEND_DESC_SIZE * (config->send_queue_size+1);
|
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);
|
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);
|
sdio_slave_hal_init(context.hal);
|
||||||
|
|
||||||
// in theory we can queue infinite buffers in the linked list, but for multi-core reason we have to use a queue to
|
// in theory we can queue infinite buffers in the linked list, but for multi-core reason we have to use a queue to
|
||||||
// count the finished buffers.
|
// count the finished buffers.
|
||||||
context.recv_event = xSemaphoreCreateCounting(UINT32_MAX, 0);
|
context.recv_event = xSemaphoreCreateCounting(UINT32_MAX, 0);
|
||||||
for(int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
if (i < 8) {
|
if (i < 8) {
|
||||||
context.events[i] = xSemaphoreCreateBinary();
|
context.events[i] = xSemaphoreCreateBinary();
|
||||||
} //for 8, already created.
|
} //for 8, already created.
|
||||||
@ -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);
|
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*));
|
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;
|
return ESP_OK;
|
||||||
|
|
||||||
@ -289,11 +299,11 @@ static inline esp_err_t sdio_slave_hw_init(sdio_slave_config_t *config)
|
|||||||
configure_pin(slot->clk_gpio, slot->func, false); //clk doesn't need a pullup
|
configure_pin(slot->clk_gpio, slot->func, false); //clk doesn't need a pullup
|
||||||
configure_pin(slot->cmd_gpio, slot->func, pullup);
|
configure_pin(slot->cmd_gpio, slot->func, pullup);
|
||||||
configure_pin(slot->d0_gpio, slot->func, pullup);
|
configure_pin(slot->d0_gpio, slot->func, pullup);
|
||||||
if ((config->flags & SDIO_SLAVE_FLAG_HOST_INTR_DISABLED)==0) {
|
if ((config->flags & SDIO_SLAVE_FLAG_HOST_INTR_DISABLED) == 0) {
|
||||||
configure_pin(slot->d1_gpio, slot->func, pullup);
|
configure_pin(slot->d1_gpio, slot->func, pullup);
|
||||||
}
|
}
|
||||||
if ((config->flags & SDIO_SLAVE_FLAG_DAT2_DISABLED)==0) {
|
if ((config->flags & SDIO_SLAVE_FLAG_DAT2_DISABLED) == 0) {
|
||||||
configure_pin(slot->d2_gpio, slot->func, pullup);
|
configure_pin(slot->d2_gpio, slot->func, pullup);
|
||||||
}
|
}
|
||||||
configure_pin(slot->d3_gpio, slot->func, pullup);
|
configure_pin(slot->d3_gpio, slot->func, pullup);
|
||||||
|
|
||||||
@ -335,14 +345,20 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
|
|||||||
intr_handle_t intr_handle = NULL;
|
intr_handle_t intr_handle = NULL;
|
||||||
const int flags = 0;
|
const int flags = 0;
|
||||||
r = esp_intr_alloc(ETS_SLC0_INTR_SOURCE, flags, sdio_intr, NULL, &intr_handle);
|
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);
|
r = init_context(config);
|
||||||
if (r != ESP_OK) return r;
|
if (r != ESP_OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
context.intr_handle = intr_handle;
|
context.intr_handle = intr_handle;
|
||||||
|
|
||||||
r = sdio_slave_hw_init(config);
|
r = sdio_slave_hw_init(config);
|
||||||
if (r != ESP_OK) return r;
|
if (r != ESP_OK) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
sdio_slave_reset();
|
sdio_slave_reset();
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -361,12 +377,14 @@ void sdio_slave_deinit(void)
|
|||||||
}
|
}
|
||||||
//unregister all buffers that is loaded and not returned
|
//unregister all buffers that is loaded and not returned
|
||||||
while (1) {
|
while (1) {
|
||||||
desc = (recv_desc_t*)sdio_slave_hal_recv_unload_desc(context.hal);
|
desc = (recv_desc_t *)sdio_slave_hal_recv_unload_desc(context.hal);
|
||||||
if (desc == NULL) break;
|
if (desc == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
free(desc);
|
free(desc);
|
||||||
}
|
}
|
||||||
esp_err_t ret = esp_intr_free(context.intr_handle);
|
esp_err_t ret = esp_intr_free(context.intr_handle);
|
||||||
assert(ret==ESP_OK);
|
assert(ret == ESP_OK);
|
||||||
(void)ret;
|
(void)ret;
|
||||||
context.intr_handle = NULL;
|
context.intr_handle = NULL;
|
||||||
deinit_context();
|
deinit_context();
|
||||||
@ -378,13 +396,17 @@ esp_err_t sdio_slave_start(void)
|
|||||||
sdio_slave_hostint_t intr = (sdio_slave_hostint_t)UINT32_MAX;
|
sdio_slave_hostint_t intr = (sdio_slave_hostint_t)UINT32_MAX;
|
||||||
sdio_slave_hal_hostint_clear(context.hal, &intr);
|
sdio_slave_hal_hostint_clear(context.hal, &intr);
|
||||||
ret = sdio_slave_hal_send_start(context.hal);
|
ret = sdio_slave_hal_send_start(context.hal);
|
||||||
if (ret != ESP_OK) return ret;
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
critical_enter_recv();
|
critical_enter_recv();
|
||||||
sdio_slave_hal_recv_start(context.hal);
|
sdio_slave_hal_recv_start(context.hal);
|
||||||
critical_exit_recv();
|
critical_exit_recv();
|
||||||
ret = ESP_OK;
|
ret = ESP_OK;
|
||||||
if (ret != ESP_OK) return ret;
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
sdio_slave_hal_set_ioready(context.hal, true);
|
sdio_slave_hal_set_ioready(context.hal, true);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -422,7 +444,7 @@ void sdio_slave_stop(void)
|
|||||||
sdio_slave_hal_recv_stop(context.hal);
|
sdio_slave_hal_recv_stop(context.hal);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdio_intr(void* arg)
|
static void sdio_intr(void *arg)
|
||||||
{
|
{
|
||||||
sdio_intr_send(arg);
|
sdio_intr_send(arg);
|
||||||
sdio_intr_recv(arg);
|
sdio_intr_recv(arg);
|
||||||
@ -432,18 +454,22 @@ static void sdio_intr(void* arg)
|
|||||||
/*---------------------------------------------------------------------------
|
/*---------------------------------------------------------------------------
|
||||||
* Host
|
* Host
|
||||||
*--------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------*/
|
||||||
static void sdio_intr_host(void* arg)
|
static void sdio_intr_host(void *arg)
|
||||||
{
|
{
|
||||||
sdio_slave_ll_slvint_t int_val;
|
sdio_slave_ll_slvint_t int_val;
|
||||||
sdio_slave_hal_slvint_fetch_clear(context.hal, &int_val);
|
sdio_slave_hal_slvint_fetch_clear(context.hal, &int_val);
|
||||||
portBASE_TYPE yield = pdFALSE;
|
portBASE_TYPE yield = pdFALSE;
|
||||||
for(int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if (BIT(i) & int_val) {
|
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);
|
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)
|
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)
|
uint8_t sdio_slave_read_reg(int pos)
|
||||||
{
|
{
|
||||||
if (pos >= 28 && pos <= 31) SDIO_SLAVE_LOGW("%s: interrupt reg, for reference", __FUNCTION__);
|
if (pos >= 28 && pos <= 31) {
|
||||||
if (pos < 0 || pos >= 64) SDIO_SLAVE_LOGE("read register address wrong");
|
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);
|
return sdio_slave_hal_host_get_reg(context.hal, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,7 +545,7 @@ esp_err_t sdio_slave_send_host_int(uint8_t pos)
|
|||||||
* If driver is stopped, the link list is stopped as well as the ISR invoker.
|
* If driver is stopped, the link list is stopped as well as the ISR invoker.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void sdio_intr_send(void* arg)
|
static void sdio_intr_send(void *arg)
|
||||||
{
|
{
|
||||||
ESP_EARLY_LOGV(TAG, "intr_send");
|
ESP_EARLY_LOGV(TAG, "intr_send");
|
||||||
portBASE_TYPE yield = pdFALSE;
|
portBASE_TYPE yield = pdFALSE;
|
||||||
@ -541,7 +571,7 @@ static void sdio_intr_send(void* arg)
|
|||||||
assert(ret == pdTRUE);
|
assert(ret == pdTRUE);
|
||||||
}
|
}
|
||||||
//get_next_finished_arg returns the total amount of returned descs.
|
//get_next_finished_arg returns the total amount of returned descs.
|
||||||
for(size_t i = 0; i < returned_cnt; i++) {
|
for (size_t i = 0; i < returned_cnt; i++) {
|
||||||
ret = xSemaphoreGiveFromISR(context.remain_cnt, &yield);
|
ret = xSemaphoreGiveFromISR(context.remain_cnt, &yield);
|
||||||
assert(ret == pdTRUE);
|
assert(ret == pdTRUE);
|
||||||
}
|
}
|
||||||
@ -549,44 +579,58 @@ static void sdio_intr_send(void* arg)
|
|||||||
|
|
||||||
sdio_slave_hal_send_new_packet_if_exist(context.hal);
|
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)
|
esp_err_t sdio_slave_send_queue(uint8_t *addr, size_t len, void *arg, TickType_t wait)
|
||||||
{
|
{
|
||||||
SDIO_SLAVE_CHECK(len > 0, "len <= 0", ESP_ERR_INVALID_ARG);
|
SDIO_SLAVE_CHECK(len > 0, "len <= 0", ESP_ERR_INVALID_ARG);
|
||||||
SDIO_SLAVE_CHECK(esp_ptr_dma_capable(addr) && (uint32_t)addr%4==0, "buffer to send should be DMA capable and 32-bit aligned",
|
SDIO_SLAVE_CHECK(esp_ptr_dma_capable(addr) && (uint32_t)addr % 4 == 0, "buffer to send should be DMA capable and 32-bit aligned",
|
||||||
ESP_ERR_INVALID_ARG);
|
ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
portBASE_TYPE cnt_ret = xSemaphoreTake(context.remain_cnt, wait);
|
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);
|
portENTER_CRITICAL(&context.write_spinlock);
|
||||||
esp_err_t ret = sdio_slave_hal_send_queue(context.hal, addr, len, arg);
|
esp_err_t ret = sdio_slave_hal_send_queue(context.hal, addr, len, arg);
|
||||||
portEXIT_CRITICAL(&context.write_spinlock);
|
portEXIT_CRITICAL(&context.write_spinlock);
|
||||||
if (ret != ESP_OK) return ret;
|
if (ret != ESP_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t sdio_slave_send_get_finished(void** out_arg, TickType_t wait)
|
esp_err_t sdio_slave_send_get_finished(void **out_arg, TickType_t wait)
|
||||||
{
|
{
|
||||||
void* arg = NULL;
|
void *arg = NULL;
|
||||||
portBASE_TYPE err = xQueueReceive(context.ret_queue, &arg, wait);
|
portBASE_TYPE err = xQueueReceive(context.ret_queue, &arg, wait);
|
||||||
if (out_arg) *out_arg = arg;
|
if (out_arg) {
|
||||||
if (err != pdTRUE) return ESP_ERR_TIMEOUT;
|
*out_arg = arg;
|
||||||
|
}
|
||||||
|
if (err != pdTRUE) {
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t sdio_slave_transmit(uint8_t* addr, size_t len)
|
esp_err_t sdio_slave_transmit(uint8_t *addr, size_t len)
|
||||||
{
|
{
|
||||||
uint32_t timestamp = cpu_hal_get_cycle_count();
|
uint32_t timestamp = cpu_hal_get_cycle_count();
|
||||||
uint32_t ret_stamp;
|
uint32_t ret_stamp;
|
||||||
|
|
||||||
esp_err_t err = sdio_slave_send_queue(addr, len, (void*)timestamp, portMAX_DELAY);
|
esp_err_t err = sdio_slave_send_queue(addr, len, (void *)timestamp, portMAX_DELAY);
|
||||||
if (err != ESP_OK) return err;
|
if (err != ESP_OK) {
|
||||||
err = sdio_slave_send_get_finished((void**)&ret_stamp, portMAX_DELAY);
|
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;
|
||||||
|
}
|
||||||
SDIO_SLAVE_CHECK(ret_stamp == timestamp, "already sent without return before", ESP_ERR_INVALID_STATE);
|
SDIO_SLAVE_CHECK(ret_stamp == timestamp, "already sent without return before", ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -642,9 +686,11 @@ static inline void critical_exit_recv(void)
|
|||||||
// remove data, still increase the counter
|
// remove data, still increase the counter
|
||||||
static esp_err_t recv_flush_data(void)
|
static esp_err_t recv_flush_data(void)
|
||||||
{
|
{
|
||||||
while(1) {
|
while (1) {
|
||||||
portBASE_TYPE ret = xSemaphoreTake(context.recv_event, 0);
|
portBASE_TYPE ret = xSemaphoreTake(context.recv_event, 0);
|
||||||
if (ret == pdFALSE) break;
|
if (ret == pdFALSE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
critical_enter_recv();
|
critical_enter_recv();
|
||||||
sdio_slave_hal_recv_flush_one_buffer(context.hal);
|
sdio_slave_hal_recv_flush_one_buffer(context.hal);
|
||||||
critical_exit_recv();
|
critical_exit_recv();
|
||||||
@ -652,7 +698,7 @@ static esp_err_t recv_flush_data(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdio_intr_recv(void* arg)
|
static void sdio_intr_recv(void *arg)
|
||||||
{
|
{
|
||||||
portBASE_TYPE yield = 0;
|
portBASE_TYPE yield = 0;
|
||||||
while (sdio_slave_hal_recv_done(context.hal)) {
|
while (sdio_slave_hal_recv_done(context.hal)) {
|
||||||
@ -667,12 +713,14 @@ static void sdio_intr_recv(void* arg)
|
|||||||
// if no more items on the list, go back and check again the interrupt,
|
// if no more items on the list, go back and check again the interrupt,
|
||||||
// will loop until the interrupt bit is kept cleared.
|
// 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)
|
esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle)
|
||||||
{
|
{
|
||||||
recv_desc_t *desc = (recv_desc_t*)handle;
|
recv_desc_t *desc = (recv_desc_t *)handle;
|
||||||
CHECK_HANDLE_IDLE(desc);
|
CHECK_HANDLE_IDLE(desc);
|
||||||
assert(desc->not_receiving);
|
assert(desc->not_receiving);
|
||||||
|
|
||||||
@ -686,9 +734,9 @@ esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle)
|
|||||||
|
|
||||||
sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)
|
sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)
|
||||||
{
|
{
|
||||||
SDIO_SLAVE_CHECK(esp_ptr_dma_capable(start) && (uint32_t)start%4==0,
|
SDIO_SLAVE_CHECK(esp_ptr_dma_capable(start) && (uint32_t)start % 4 == 0,
|
||||||
"buffer to register should be DMA capable and 32-bit aligned", NULL);
|
"buffer to register should be DMA capable and 32-bit aligned", NULL);
|
||||||
recv_desc_t *desc = (recv_desc_t*)heap_caps_malloc(sizeof(recv_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
recv_desc_t *desc = (recv_desc_t *)heap_caps_malloc(sizeof(recv_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||||
if (desc == NULL) {
|
if (desc == NULL) {
|
||||||
SDIO_SLAVE_LOGE("cannot allocate lldesc for new buffer");
|
SDIO_SLAVE_LOGE("cannot allocate lldesc for new buffer");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -702,28 +750,52 @@ sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t* handle_ret, uint8_t **out_addr, size_t *out_len, TickType_t wait)
|
esp_err_t sdio_slave_recv(sdio_slave_buf_handle_t *handle_ret, uint8_t **out_addr, size_t *out_len, TickType_t wait)
|
||||||
|
{
|
||||||
|
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);
|
SDIO_SLAVE_CHECK(handle_ret != NULL, "handle address cannot be 0", ESP_ERR_INVALID_ARG);
|
||||||
portBASE_TYPE ret = xSemaphoreTake(context.recv_event, wait);
|
portBASE_TYPE err = xSemaphoreTake(context.recv_event, wait);
|
||||||
if (ret == pdFALSE) return ESP_ERR_TIMEOUT;
|
if (err == pdFALSE) {
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
critical_enter_recv();
|
critical_enter_recv();
|
||||||
//remove from queue, add back to reg list.
|
//remove from queue, add back to reg list.
|
||||||
recv_desc_t *desc = (recv_desc_t*)sdio_slave_hal_recv_unload_desc(context.hal);
|
recv_desc_t *desc = (recv_desc_t *)sdio_slave_hal_recv_unload_desc(context.hal);
|
||||||
assert(desc != NULL && desc->hal_desc.owner == 0);
|
assert(desc != NULL && desc->hal_desc.owner == 0);
|
||||||
TAILQ_INSERT_TAIL(&context.recv_reg_list, desc, te);
|
TAILQ_INSERT_TAIL(&context.recv_reg_list, desc, te);
|
||||||
critical_exit_recv();
|
critical_exit_recv();
|
||||||
|
|
||||||
*handle_ret = (sdio_slave_buf_handle_t)desc;
|
*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;
|
if (!desc->hal_desc.eof) {
|
||||||
return ESP_OK;
|
ret = ESP_ERR_NOT_FINISHED;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)
|
esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)
|
||||||
{
|
{
|
||||||
recv_desc_t *desc = (recv_desc_t*)handle;
|
recv_desc_t *desc = (recv_desc_t *)handle;
|
||||||
CHECK_HANDLE_IDLE(desc); //in the queue, fail.
|
CHECK_HANDLE_IDLE(desc); //in the queue, fail.
|
||||||
|
|
||||||
critical_enter_recv();
|
critical_enter_recv();
|
||||||
@ -733,11 +805,15 @@ esp_err_t sdio_slave_recv_unregister_buf(sdio_slave_buf_handle_t handle)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* sdio_slave_recv_get_buf(sdio_slave_buf_handle_t handle, size_t *len_o)
|
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) {
|
||||||
recv_desc_t *desc = (recv_desc_t*)handle;
|
return NULL;
|
||||||
|
}
|
||||||
|
recv_desc_t *desc = (recv_desc_t *)handle;
|
||||||
|
|
||||||
if (len_o!= NULL) *len_o= desc->hal_desc.length;
|
if (len_o != NULL) {
|
||||||
return (uint8_t*)desc->hal_desc.buf;
|
*len_o = desc->hal_desc.length;
|
||||||
|
}
|
||||||
|
return (uint8_t *)desc->hal_desc.buf;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ typedef int esp_err_t;
|
|||||||
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
|
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
|
||||||
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
|
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
|
||||||
#define ESP_ERR_INVALID_MAC 0x10B /*!< MAC address 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_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
|
||||||
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
|
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
|
||||||
#define ESP_ERR_NOT_FINISHED 0x201 ///< There is still remaining data.
|
|
||||||
|
|
||||||
struct essl_dev_t;
|
struct essl_dev_t;
|
||||||
/// Handle of an ESSL device
|
/// Handle of an ESSL device
|
||||||
typedef struct essl_dev_t* essl_handle_t;
|
typedef struct essl_dev_t* essl_handle_t;
|
||||||
|
@ -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:
|
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
|
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``.
|
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.
|
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
|
.. note:: To avoid overhead from copying data, the driver itself doesn't have any buffer inside, the application is
|
||||||
|
@ -186,9 +186,8 @@ void app_main(void)
|
|||||||
|
|
||||||
sdio_slave_write_reg(0, JOB_IDLE);
|
sdio_slave_write_reg(0, JOB_IDLE);
|
||||||
|
|
||||||
sdio_slave_buf_handle_t handle;
|
|
||||||
for(int i = 0; i < BUFFER_NUM; i++) {
|
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);
|
assert(handle != NULL);
|
||||||
|
|
||||||
ret = sdio_slave_recv_load_buf(handle);
|
ret = sdio_slave_recv_load_buf(handle);
|
||||||
@ -211,35 +210,72 @@ void app_main(void)
|
|||||||
ESP_LOGI(TAG, EV_STR("slave ready"));
|
ESP_LOGI(TAG, EV_STR("slave ready"));
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
//receive data and send back to host.
|
const TickType_t non_blocking = 0, blocking = portMAX_DELAY;
|
||||||
size_t length;
|
sdio_slave_buf_handle_t recv_queue[BUFFER_NUM];
|
||||||
uint8_t *ptr;
|
int packet_size = 0;
|
||||||
|
|
||||||
const TickType_t non_blocking = 0;
|
sdio_slave_buf_handle_t handle;
|
||||||
ret = sdio_slave_recv(&handle, &ptr, &length, non_blocking);
|
ret = sdio_slave_recv_packet(&handle, non_blocking);
|
||||||
if (ret == ESP_OK) {
|
if (ret != ESP_ERR_TIMEOUT) {
|
||||||
ESP_LOGI(TAG, "handle: %p, recv len: %d, data:", handle, length);
|
recv_queue[packet_size++] = handle;
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//send the received buffer to host, with the handle as the argument
|
//Receive following buffers in the same packet in blocking mode.
|
||||||
ret = sdio_slave_send_queue(ptr, length, handle, non_blocking);
|
//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.
|
||||||
if (ret == ESP_ERR_TIMEOUT) {
|
while (ret == ESP_ERR_NOT_FINISHED) {
|
||||||
// send failed, direct return the buffer to rx
|
//The return value must be ESP_OK or ESP_ERR_NOT_FINISHED.
|
||||||
ESP_LOGE(TAG, "send_queue full, discard received.");
|
ret = sdio_slave_recv_packet(&handle, blocking);
|
||||||
ret = sdio_slave_recv_load_buf(handle);
|
recv_queue[packet_size++] = handle;
|
||||||
}
|
}
|
||||||
ESP_ERROR_CHECK(ret);
|
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 = sdio_slave_recv_get_buf(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, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//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.");
|
||||||
|
ret = sdio_slave_recv_load_buf(handle);
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there's finished sending desc, return the buffer to receiving driver
|
// if there's finished sending desc, return the buffer to receiving driver
|
||||||
for(;;){
|
for(;;){
|
||||||
sdio_slave_buf_handle_t handle;
|
void* send_args = NULL;
|
||||||
ret = sdio_slave_send_get_finished(&handle, 0);
|
ret = sdio_slave_send_get_finished(&send_args, 0);
|
||||||
if (ret == ESP_ERR_TIMEOUT) break;
|
|
||||||
|
//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);
|
ESP_ERROR_CHECK(ret);
|
||||||
ret = sdio_slave_recv_load_buf(handle);
|
ret = sdio_slave_recv_load_buf(handle);
|
||||||
ESP_ERROR_CHECK(ret);
|
ESP_ERROR_CHECK(ret);
|
||||||
|
Loading…
Reference in New Issue
Block a user