diff --git a/components/driver/sdio_slave.c b/components/driver/sdio_slave.c index 98e4f4ae40..317e492bc5 100644 --- a/components/driver/sdio_slave.c +++ b/components/driver/sdio_slave.c @@ -162,7 +162,7 @@ typedef enum { ringbuf_free_ptr = offset_of(sdio_ringbuf_t, free_ptr), } sdio_ringbuf_pointer_t; -#define SDIO_RINGBUF_INITIALIZER() (sdio_ringbuf_t){.write_spinlock = portMUX_INITIALIZER_UNLOCKED,} +#define SDIO_RINGBUF_INITIALIZER {.write_spinlock = portMUX_INITIALIZER_UNLOCKED,} typedef struct { sdio_slave_config_t config; @@ -191,24 +191,26 @@ typedef struct { portMUX_TYPE recv_spinlock; } sdio_context_t; -static sdio_context_t context = { - .intr_handle = NULL, - /*------- events ---------------*/ - .events = {}, - .reg_spinlock = portMUX_INITIALIZER_UNLOCKED, - /*------- sending ---------------*/ - .send_state = STATE_IDLE, - .sendbuf = SDIO_RINGBUF_INITIALIZER(), - .ret_queue = NULL, - .in_flight = NULL, - .in_flight_end = NULL, - .in_flight_next = NULL, - /*------- receiving ---------------*/ - .recv_link_list = STAILQ_HEAD_INITIALIZER(context.recv_link_list), - .recv_reg_list = TAILQ_HEAD_INITIALIZER(context.recv_reg_list), - .recv_cur_ret = NULL, - .recv_spinlock = portMUX_INITIALIZER_UNLOCKED, -}; +#define CONTEXT_INIT_VAL { \ + .intr_handle = NULL, \ + /*------- events ---------------*/ \ + .events = {}, \ + .reg_spinlock = portMUX_INITIALIZER_UNLOCKED, \ + /*------- sending ---------------*/ \ + .send_state = STATE_IDLE, \ + .sendbuf = SDIO_RINGBUF_INITIALIZER, \ + .ret_queue = NULL, \ + .in_flight = NULL, \ + .in_flight_end = NULL, \ + .in_flight_next = NULL, \ + /*------- receiving ---------------*/ \ + .recv_link_list = STAILQ_HEAD_INITIALIZER(context.recv_link_list), \ + .recv_reg_list = TAILQ_HEAD_INITIALIZER(context.recv_reg_list), \ + .recv_cur_ret = NULL, \ + .recv_spinlock = portMUX_INITIALIZER_UNLOCKED, \ +} + +static sdio_context_t context = CONTEXT_INIT_VAL; static void sdio_intr(void*); static void sdio_intr_host(void*); @@ -238,7 +240,7 @@ static void sdio_ringbuf_deinit(sdio_ringbuf_t* buf) { if (buf->remain_cnt != NULL) vSemaphoreDelete(buf->remain_cnt); if (buf->data != NULL) free(buf->data); - *buf = SDIO_RINGBUF_INITIALIZER(); + *buf = (sdio_ringbuf_t) SDIO_RINGBUF_INITIALIZER; } static esp_err_t sdio_ringbuf_init(sdio_ringbuf_t* buf, int item_size, int item_cnt) @@ -251,7 +253,7 @@ static esp_err_t sdio_ringbuf_init(sdio_ringbuf_t* buf, int item_size, int item_ //one item is not used. buf->size = item_size * (item_cnt+1); //apply for resources - buf->data = (uint8_t*)malloc(buf->size); + buf->data = (uint8_t*)heap_caps_malloc(buf->size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (buf->data == NULL) goto no_mem; buf->remain_cnt = xSemaphoreCreateCounting(item_cnt, item_cnt); if (buf->remain_cnt == NULL) goto no_mem; @@ -451,6 +453,7 @@ static esp_err_t init_context(sdio_slave_config_t *config) { SDIO_SLAVE_CHECK(*(uint32_t*)&context.config == 0, "sdio slave already initialized", ESP_ERR_INVALID_STATE); + context = (sdio_context_t)CONTEXT_INIT_VAL; context.config = *config; // in theory we can queue infinite buffers in the linked list, but for multi-core reason we have to use a queue to @@ -472,8 +475,6 @@ static esp_err_t init_context(sdio_slave_config_t *config) context.ret_queue = xQueueCreate(config->send_queue_size, sizeof(void*)); if (context.ret_queue == NULL) goto no_mem; - context.recv_link_list = (buf_stailq_t)STAILQ_HEAD_INITIALIZER(context.recv_link_list); - context.recv_reg_list = (buf_tailq_t)TAILQ_HEAD_INITIALIZER(context.recv_reg_list); return ESP_OK; no_mem: @@ -485,9 +486,9 @@ static void configure_pin(int pin, uint32_t func, bool pullup) { const int sdmmc_func = func; const int drive_strength = 3; - assert(pin!=-1); + assert(pin != -1); uint32_t reg = GPIO_PIN_MUX_REG[pin]; - assert(reg!=UINT32_MAX); + assert(reg != UINT32_MAX); PIN_INPUT_ENABLE(reg); PIN_FUNC_SELECT(reg, sdmmc_func); @@ -574,6 +575,29 @@ static inline esp_err_t sdio_slave_hw_init(sdio_slave_config_t *config) return ESP_OK; } +static void recover_pin(int pin, int sdio_func) +{ + uint32_t reg = GPIO_PIN_MUX_REG[pin]; + assert(reg != UINT32_MAX); + + int func = REG_GET_FIELD(reg, MCU_SEL); + if (func == sdio_func) { + gpio_set_direction(pin, GPIO_MODE_INPUT); + PIN_FUNC_SELECT(reg, PIN_FUNC_GPIO); + } +} + +static void sdio_slave_hw_deinit(void) +{ + const sdio_slave_slot_info_t *slot = &sdio_slave_slot_info[1]; + recover_pin(slot->clk_gpio, slot->func); + recover_pin(slot->cmd_gpio, slot->func); + recover_pin(slot->d0_gpio, slot->func); + recover_pin(slot->d1_gpio, slot->func); + recover_pin(slot->d2_gpio, slot->func); + recover_pin(slot->d3_gpio, slot->func); +} + esp_err_t sdio_slave_initialize(sdio_slave_config_t *config) { esp_err_t r; @@ -594,6 +618,20 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config) void sdio_slave_deinit(void) { + sdio_slave_hw_deinit(); + + //unregister all buffers in the queue, and not in the queue + buf_desc_t *temp_desc; + buf_desc_t *desc; + TAILQ_FOREACH_SAFE(desc, &context.recv_reg_list, te, temp_desc) { + TAILQ_REMOVE(&context.recv_reg_list, desc, te); + free(desc); + } + STAILQ_FOREACH_SAFE(desc, &context.recv_link_list, qe, temp_desc) { + STAILQ_REMOVE(&context.recv_link_list, desc, buf_desc_s, qe); + free(desc); + } + esp_err_t ret = esp_intr_free(context.intr_handle); assert(ret==ESP_OK); context.intr_handle = NULL; @@ -1165,9 +1203,9 @@ static void sdio_intr_recv(void* arg) // This may cause the ``cur_ret`` pointer to be NULL, indicating the list is empty, // in this case the ``tx_done`` should happen no longer until new desc is appended. // The app is responsible to place the pointer to the right place again when appending new desc. - critical_enter_recv(); + portENTER_CRITICAL_ISR(&context.recv_spinlock); context.recv_cur_ret = STAILQ_NEXT(context.recv_cur_ret, qe); - critical_exit_recv(); + portEXIT_CRITICAL_ISR(&context.recv_spinlock); ESP_EARLY_LOGV(TAG, "intr_recv: Give"); xSemaphoreGiveFromISR(context.recv_event, &yield); SLC.slc0_int_clr.tx_done = 1; @@ -1201,6 +1239,7 @@ esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle) SLC.slc0_tx_link.addr = (uint32_t)desc; SLC.slc0_tx_link.start = 1; ESP_LOGV(TAG, "recv_load_buf: start new"); + SLC.slc0_int_ena.tx_done = 1; } else { //restart former ll operation SLC.slc0_tx_link.restart = 1; @@ -1216,7 +1255,7 @@ 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, "buffer to register should be DMA capable and 32-bit aligned", NULL); - buf_desc_t *desc = (buf_desc_t*)malloc(sizeof(buf_desc_t)); + buf_desc_t *desc = (buf_desc_t*)heap_caps_malloc(sizeof(buf_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (desc == NULL) { SDIO_SLAVE_LOGE("cannot allocate lldesc for new buffer"); return NULL; diff --git a/components/driver/sdspi_host.c b/components/driver/sdspi_host.c index e2780dd04b..b9e32ea60c 100644 --- a/components/driver/sdspi_host.c +++ b/components/driver/sdspi_host.c @@ -241,6 +241,23 @@ esp_err_t sdspi_host_deinit(void) s_slots[i].transactions = NULL; spi_bus_free((spi_host_device_t) i); s_slots[i].handle = NULL; + + uint64_t pin_bit_mask = BIT64(s_slots[i].gpio_cs); + if (s_slots[i].gpio_cd != GPIO_UNUSED) { + pin_bit_mask |= BIT64(s_slots[i].gpio_cd); + } + if (s_slots[i].gpio_wp != GPIO_UNUSED) { + pin_bit_mask |= BIT64(s_slots[i].gpio_wp); + } + if (s_slots[i].gpio_int != GPIO_UNUSED) { + pin_bit_mask |= BIT64(s_slots[i].gpio_int); + } + + gpio_config_t config = { + .pin_bit_mask = pin_bit_mask, + .mode = GPIO_MODE_INPUT, + }; + gpio_config(&config); } if (s_slots[i].semphr_int) { vSemaphoreDelete(s_slots[i].semphr_int); diff --git a/components/driver/test/CMakeLists.txt b/components/driver/test/CMakeLists.txt index f6245349c2..9bc60c2c20 100644 --- a/components/driver/test/CMakeLists.txt +++ b/components/driver/test/CMakeLists.txt @@ -5,5 +5,5 @@ endif() idf_component_register(SRC_DIRS ${srcdirs} INCLUDE_DIRS include param_test/include - REQUIRES unity test_utils driver nvs_flash + REQUIRES unity test_utils driver nvs_flash esp_serial_slave_link ) diff --git a/components/driver/test/esp32/test_sdio.c b/components/driver/test/esp32/test_sdio.c new file mode 100644 index 0000000000..3a04602e47 --- /dev/null +++ b/components/driver/test/esp32/test_sdio.c @@ -0,0 +1,769 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "unity.h" +#include "esp_serial_slave_link/essl_sdio.h" +#include "driver/sdio_slave.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "test_utils.h" +#include "param_test.h" +#include "esp_log.h" + + +#define TIMEOUT_MAX UINT32_MAX +#define INT_MASK_ALL 0xff +#define SDIO_SLAVE_QUEUE_SIZE 20 +#define RX_BUFFER_SIZE 2048 +#define RX_BUFFER_NUM 10 +#define TX_BUFFER_SIZE 2048 +#define REG_ADDR_MAX 60 +//the test should run accross the boundary, i.e. over 0x100000 bytes. +//TEST_CNT > 512 +#define TEST_CNT 10000 + +#define TEST_RESET_DATA_LEN 10 + +#ifndef MIN +#define MIN(a, b) ((a)<(b)? (a): (b)) +#endif + +typedef enum { + SDIO_1BIT = 0, + SDIO_4BIT = 1, + SDIO_SPI = 2, +} sdio_mode_t; + +typedef void (*sdio_test_func)(essl_handle_t handle); + +typedef struct { + const char test_name[16]; + sdio_mode_t sdio_mode; + uint32_t freq; + bool check_data; + bool packet_mode; +} sdio_test_config_t; + +sdio_test_config_t test_cfg_array[] = { + //the first item will be the default config used by all tests + { + .test_name = "HS4B", + .sdio_mode = SDIO_4BIT, + .freq = SDMMC_FREQ_HIGHSPEED, + .check_data = true, + }, + { + .test_name = "HS1B", + .sdio_mode = SDIO_1BIT, + .freq = SDMMC_FREQ_HIGHSPEED, + .check_data = true, + }, + { + .test_name = "SPI", + .sdio_mode = SDIO_SPI, + .freq = SDMMC_FREQ_HIGHSPEED, + .check_data = true, + }, + //the performance test is only done when psram is not enabled +#if !CONFIG_SPIRAM && !CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE + { + .test_name = "HS4B (perf)", + .sdio_mode = SDIO_4BIT, + .freq = SDMMC_FREQ_HIGHSPEED, + }, + { + .test_name = "HS1B (perf)", + .sdio_mode = SDIO_1BIT, + .freq = SDMMC_FREQ_HIGHSPEED, + }, + { + .test_name = "SPI (perf)", + .sdio_mode = SDIO_SPI, + .freq = SDMMC_FREQ_HIGHSPEED, + }, +#endif +}; + +sdio_test_config_t packet_config = { + .test_name = "HS4B packet", + .sdio_mode = SDIO_4BIT, + .freq = SDMMC_FREQ_HIGHSPEED, + .check_data = true, + .packet_mode = true, +}; + +const sdio_test_config_t* default_config = &test_cfg_array[0]; + +#define TEST_SIZE (sizeof(test_cfg_array)/sizeof(sdio_test_config_t)) + +static const char MASTER_TAG[] = "master"; +static const char SLAVE_TAG[] = "slave"; + +/******************************************************************************* + * Master + ******************************************************************************/ + +static sdmmc_card_t s_card; + +typedef void (*test_func_t)(essl_handle_t handle, const sdio_test_config_t* config); + +static void init_sdmmc_host(void); +static void init_essl(essl_handle_t *out_handle, const sdio_test_config_t *conf); +static void deinit_essl(essl_handle_t handle, const sdio_test_config_t *conf); + +static void test_framework_master(test_func_t test_func, const sdio_test_config_t* config) +{ + ESP_LOGI(MASTER_TAG, "### Testing %s... ####", config->test_name); + essl_handle_t handle; + esp_err_t err; + init_essl(&handle, config); + + err = essl_init(handle, TIMEOUT_MAX); + TEST_ESP_OK(err); + + (*test_func)(handle, config); + + deinit_essl(handle, config); +} + +static void init_sdmmc_host(void) +{ + esp_err_t err; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + err = sdmmc_host_init(); + TEST_ESP_OK(err); + + err = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + TEST_ESP_OK(err); +} + +static void init_essl(essl_handle_t *out_handle, const sdio_test_config_t *conf) +{ + sdmmc_host_t config; + esp_err_t err; + /* Probe */ + + switch (conf->sdio_mode) { + case SDIO_4BIT: + ESP_LOGI(MASTER_TAG, "Probe using SD 4-bit...\n"); + config = (sdmmc_host_t)SDMMC_HOST_DEFAULT(); + config.flags = SDMMC_HOST_FLAG_4BIT; + config.max_freq_khz = conf->freq; + init_sdmmc_host(); + break; + case SDIO_1BIT: + ESP_LOGI(MASTER_TAG, "Probe using SD 1-bit...\n"); + config = (sdmmc_host_t)SDMMC_HOST_DEFAULT(); + config.flags = SDMMC_HOST_FLAG_1BIT; + config.max_freq_khz = conf->freq; + init_sdmmc_host(); + break; + case SDIO_SPI: + config = (sdmmc_host_t)SDSPI_HOST_DEFAULT(); + + sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); + slot_config.gpio_miso = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D0; + slot_config.gpio_mosi = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_CMD; + slot_config.gpio_sck = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_CLK; + slot_config.gpio_cs = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D3; + slot_config.gpio_int = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D1; + + err = gpio_install_isr_service(0); + TEST_ASSERT(err == ESP_OK || err == ESP_ERR_INVALID_STATE); + + err = sdspi_host_init(); + TEST_ESP_OK(err); + + err = sdspi_host_init_slot(HSPI_HOST, &slot_config); + TEST_ESP_OK(err); + ESP_LOGI(MASTER_TAG, "Probe using SPI...\n"); + break; + } + + sdmmc_card_t *card = &s_card; + //wait for at least 5 seconds + int retry_times = 5; + do { + if (sdmmc_card_init(&config, card) == ESP_OK) { + break; + } + ESP_LOGW(MASTER_TAG, "slave init failed, retry..."); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } while (--retry_times); + TEST_ASSERT_MESSAGE(retry_times != 0, "Initializing slave failed."); + essl_sdio_config_t ser_config = { + .card = card, + .recv_buffer_size = RX_BUFFER_SIZE, + }; + err = essl_sdio_init_dev(out_handle, &ser_config); + TEST_ESP_OK(err); + + err = essl_init(*out_handle, TIMEOUT_MAX); + TEST_ESP_OK(err); +} + +static void deinit_essl(essl_handle_t handle, const sdio_test_config_t *conf) +{ + esp_err_t err; + essl_sdio_deinit_dev(handle); + if (conf->sdio_mode == SDIO_SPI) { + gpio_uninstall_isr_service(); + + err = sdspi_host_deinit(); + TEST_ESP_OK(err); + } else { + err = sdmmc_host_deinit(); + TEST_ESP_OK(err); + } +} + +static void send_finish_test(essl_handle_t handle) +{ + //the slave needs a signal to quite the test + essl_send_slave_intr(handle, BIT(7), TIMEOUT_MAX); +} + +static void test_int(essl_handle_t handle, const sdio_test_config_t* config) +{ + esp_err_t err; + err = essl_set_intr_ena(handle, INT_MASK_ALL, TIMEOUT_MAX); + TEST_ESP_OK(err); + + err = essl_wait_int(handle, 0); + TEST_ASSERT_EQUAL_HEX(ESP_ERR_TIMEOUT, err); + + //tests all 8 interrupts of the slave, in which int 7 is used to terminate the test on the slave. + for (int i = 0; i < 8; i ++) { + uint32_t int_st; + err = essl_send_slave_intr(handle, BIT(i), TIMEOUT_MAX); + TEST_ESP_OK(err); + //the slave should return interrupt with the same bit in 10 ms + err = essl_wait_int(handle, 10); + TEST_ESP_OK(err); + + err = essl_get_intr(handle, NULL, &int_st, TIMEOUT_MAX); + TEST_ESP_OK(err); + //check and clear the returned interrupt + TEST_ASSERT_EQUAL_HEX(BIT(i), int_st); + err = essl_clear_intr(handle, int_st, TIMEOUT_MAX); + TEST_ESP_OK(err); + } +} + +static void test_sdio_interrupt_master(void) +{ + test_framework_master(test_int, default_config); +} + +static void test_reg(essl_handle_t handle, const sdio_test_config_t* config) +{ + esp_err_t err; + uint8_t data[REG_ADDR_MAX]; + srand(850); + + //initialize the buffer + for (int i = 0; i < REG_ADDR_MAX; i++) { + data[i] = rand(); + err = essl_write_reg(handle, i, data[i], NULL, 10); + TEST_ESP_OK(err); + } + + for (int i = 0; i < 512; i++) { + //randomly write one + int offset = rand() % REG_ADDR_MAX; + uint8_t data_write = rand(); + data[offset] = data_write; + + err = essl_write_reg(handle, offset, data_write, NULL, 10); + TEST_ESP_OK(err); + + //randomly read another one and compare + offset = rand() % REG_ADDR_MAX; + uint8_t data_read; + err = essl_read_reg(handle, offset, &data_read, 10); + TEST_ESP_OK(err); + + TEST_ASSERT_EQUAL_HEX8(data[offset], data_read); + } + + send_finish_test(handle); +} + +static void test_sdio_reg_master(void) +{ + test_framework_master(test_reg, default_config); +} + +static uint8_t tx_buffer[TX_BUFFER_SIZE*2]; +static uint8_t rcv_buffer[RX_BUFFER_SIZE*RX_BUFFER_NUM]; + +static void init_tx_buffer(void) +{ + srand(776); + for (int i = 0; i < sizeof(tx_buffer); i++) { + tx_buffer[i] = rand(); + } +} + +static void get_master_send_data(int offset, uint8_t** out_start, int* out_len) +{ + int page_cnt = offset / TX_BUFFER_SIZE; + int offset_in_page = offset % TX_BUFFER_SIZE; + srand(page_cnt); + int page_offset = (rand() % (sizeof(tx_buffer) - (TX_BUFFER_SIZE) + 1)) & (~3); + *out_start = &tx_buffer[page_offset + offset_in_page]; + *out_len = TX_BUFFER_SIZE - offset_in_page; +} + +static void log_performance_tohost(uint32_t speed, const sdio_test_config_t* config) +{ + if (!config->check_data) { + switch (config->sdio_mode) { + case SDIO_4BIT: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_4BIT, "%d", speed); + break; + case SDIO_1BIT: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_1BIT, "%d", speed); + break; + case SDIO_SPI: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_SPI, "%d", speed); + break; + } + } + ESP_LOGI(MASTER_TAG, "Throughput: %.2lf MB/s", speed/1000.); +} + +static void test_tp_tohost_master(essl_handle_t handle, const sdio_test_config_t* config) +{ + esp_err_t err; + int expected_length = TEST_CNT * TX_BUFFER_SIZE; + int recv_size = 4096; + init_tx_buffer(); + //wait for the slave to get ready + vTaskDelay(20); + + int remain_length = expected_length; + int offset = 0; + + // though the flow is the same, the check of config->check_data influences the throughput much, put it outside + uint32_t pre = xTaskGetTickCount(); + if (config->check_data) { + do { + size_t rcv_len; + err = essl_get_packet(handle, rcv_buffer, recv_size, &rcv_len, TIMEOUT_MAX); + TEST_ASSERT(err == ESP_OK || err == ESP_ERR_NOT_FINISHED); + TEST_ASSERT_LESS_OR_EQUAL(remain_length, rcv_len); + + //compare until all received data are used + int compared_len = 0; + do { + //get the expected master sent data, there may be several segments, so get and compare + //several times + uint8_t* cmp_start; + int seg_len; + get_master_send_data(offset, &cmp_start, &seg_len); + + int cmp_len = MIN(rcv_len-compared_len, seg_len); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(cmp_start, &rcv_buffer[compared_len], cmp_len); + + compared_len += cmp_len; + offset += cmp_len; + } while (compared_len < rcv_len); + + remain_length -= rcv_len; + } while (remain_length > 0); + } else { + do { + size_t rcv_len; + err = essl_get_packet(handle, rcv_buffer, recv_size, &rcv_len, TIMEOUT_MAX); + TEST_ASSERT(err == ESP_OK || err == ESP_ERR_NOT_FINISHED); + TEST_ASSERT_LESS_OR_EQUAL(remain_length, rcv_len); + + offset += rcv_len; + remain_length -= rcv_len; + } while (remain_length > 0); + } + uint32_t end = xTaskGetTickCount(); + + uint32_t total_time_ms = (end-pre)*portTICK_PERIOD_MS; + uint32_t throughput_byte_per_ms = expected_length / total_time_ms; + ESP_LOGI(MASTER_TAG, "test done, total time: %d ms, bytes transferred: %d", total_time_ms, expected_length); + log_performance_tohost(throughput_byte_per_ms, config); + + send_finish_test(handle); +} + +static void log_performance_frhost(uint32_t speed, const sdio_test_config_t* config) +{ + if (!config->check_data) { + switch (config->sdio_mode) { + case SDIO_4BIT: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_4BIT, "%d", speed); + break; + case SDIO_1BIT: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_1BIT, "%d", speed); + break; + case SDIO_SPI: + TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_SPI, "%d", speed); + break; + } + } + ESP_LOGI(MASTER_TAG, "Throughput: %.2lf MB/s", speed/1000.); +} + +static void test_tp_frhost_master(essl_handle_t handle, const sdio_test_config_t* config) +{ + esp_err_t err; + int expected_length = TEST_CNT * TX_BUFFER_SIZE; + init_tx_buffer(); + //wait for the slave to get ready + vTaskDelay(20); + + int remain_length = expected_length; + int offset = 0; + uint32_t pre = xTaskGetTickCount(); + do { + int send_len; + uint8_t* send_start; + get_master_send_data(offset, &send_start, &send_len); + TEST_ASSERT_EQUAL(TX_BUFFER_SIZE, send_len); + + err = essl_send_packet(handle, send_start, send_len, TIMEOUT_MAX); + TEST_ASSERT(err == ESP_OK); + + remain_length -= send_len; + offset += send_len; + } while (remain_length > 0); + + uint32_t end = xTaskGetTickCount(); + + uint32_t total_time_ms = (end-pre)*portTICK_PERIOD_MS; + uint32_t throughput_byte_per_ms = expected_length / total_time_ms; + ESP_LOGI(MASTER_TAG, "test done, total time: %d ms, bytes transferred: %d", total_time_ms, expected_length); + log_performance_frhost(throughput_byte_per_ms, config); + + send_finish_test(handle); +} + +void test_reset_master(essl_handle_t handle, const sdio_test_config_t* config) +{ + init_tx_buffer(); + //wait for the slave to stop, reset and start again + vTaskDelay(10); + + for (int i = 0; i < 10; i++) { + WORD_ALIGNED_ATTR uint8_t buffer[TEST_RESET_DATA_LEN]; + size_t read_len; + esp_err_t err = essl_get_packet(handle, buffer, TEST_RESET_DATA_LEN, &read_len, portMAX_DELAY); + if (err == ESP_ERR_NOT_FINISHED) { + TEST_ASSERT_LESS_THAN(10, i); + err = ESP_OK; + } + TEST_ESP_OK(err); + TEST_ASSERT_EQUAL(TEST_RESET_DATA_LEN, read_len); + TEST_ASSERT_EQUAL_HEX8_ARRAY(tx_buffer + 4*i, buffer, read_len); + } + + for (int i = 0; i < 10; i++) { + esp_err_t err = essl_send_packet(handle, tx_buffer + i * 8, TEST_RESET_DATA_LEN, portMAX_DELAY); + TEST_ESP_OK(err); + } + + send_finish_test(handle); +} + +void test_sdio_reset_master(void) +{ + test_framework_master(test_reset_master, &packet_config); +} + +/******************************************************************************* + * Slave + ******************************************************************************/ + +typedef struct { + int queued_cnt; + bool s_finished; +} slave_context_t; + +typedef void (*test_func_slave_t)(slave_context_t *context, const sdio_test_config_t* config); + +static slave_context_t slave_context; + +static void event_cb(uint8_t event) +{ + ESP_EARLY_LOGI(SLAVE_TAG, "event: %d", event); + sdio_slave_send_host_int(event); + if (event == 7) slave_context.s_finished = true; +} + + +static void wait_for_finish(slave_context_t *ctx) +{ + while (!ctx->s_finished) { + vTaskDelay(10); + } + //wait for host to read the respond from slave + vTaskDelay(10); +} + +static void test_framework_slave(test_func_slave_t test_func, const sdio_test_config_t* config) +{ + slave_context.s_finished = false; + esp_err_t err; + sdio_slave_config_t slave_config = { + .sending_mode = (config->packet_mode? SDIO_SLAVE_SEND_PACKET: SDIO_SLAVE_SEND_STREAM), + .send_queue_size = SDIO_SLAVE_QUEUE_SIZE, + .recv_buffer_size = RX_BUFFER_SIZE, + .event_cb = event_cb, + }; + err = sdio_slave_initialize(&slave_config); + TEST_ESP_OK(err); + + err = sdio_slave_start(); + TEST_ESP_OK(err); + + ESP_LOGI(SLAVE_TAG, "slave ready"); + + test_func(&slave_context, config); + + sdio_slave_stop(); + sdio_slave_deinit(); +} + +static void test_int_slave(slave_context_t* ctx, const sdio_test_config_t* config) +{ + wait_for_finish(ctx); +} + +static void test_sdio_interrupt_slave(void) +{ + test_framework_slave(test_int_slave, default_config); +} + +static void test_tp_tohost_slave(slave_context_t* ctx, const sdio_test_config_t* config) +{ +#define QUEUE_FULL() (ctx->queued_cnt == SDIO_SLAVE_QUEUE_SIZE) +#define QUEUE_EMPTY() (ctx->queued_cnt == 0) + + init_tx_buffer(); + esp_err_t err; + int offset = 0; + for (int i = 0; i < TEST_CNT; i++) { + do { + void* arg; + //when the queue is full, do a blocking wait for 10ms, otherwise non-blocking + err = sdio_slave_send_get_finished(&arg, QUEUE_FULL()? 1: 0); + if (err == ESP_OK) { + ctx->queued_cnt --; + continue; + } + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, err); + } while (QUEUE_FULL()); + + uint8_t* start; + int send_len; + get_master_send_data(offset, &start, &send_len); + TEST_ASSERT_EQUAL(TX_BUFFER_SIZE, send_len); + + err = sdio_slave_send_queue(start, send_len, NULL, portMAX_DELAY); + TEST_ESP_OK(err); + + ctx->queued_cnt++; + offset += TX_BUFFER_SIZE; + } + + while (!QUEUE_EMPTY()) { + void* arg; + err = sdio_slave_send_get_finished(&arg, portMAX_DELAY); + TEST_ESP_OK(err); + ctx->queued_cnt--; + } + + wait_for_finish(ctx); +} + +static void slave_parepare_recv_buffer(void) +{ + for (int i = 0; i < RX_BUFFER_NUM; i++) { + sdio_slave_buf_handle_t buf_handle = sdio_slave_recv_register_buf(&rcv_buffer[i*RX_BUFFER_SIZE]); + esp_err_t err = sdio_slave_recv_load_buf(buf_handle); + TEST_ESP_OK(err); + } + +} + + +static void test_tp_frhost_slave(slave_context_t *ctx, const sdio_test_config_t* config) +{ + esp_err_t err; + init_tx_buffer(); + slave_parepare_recv_buffer(); + + int offset = 0; + for (int i = 0; i < TEST_CNT; i++) { + sdio_slave_buf_handle_t buf_handle; + uint8_t* buf; + size_t rcv_len; + + err = sdio_slave_recv(&buf_handle, &buf, &rcv_len, portMAX_DELAY); + TEST_ESP_OK(err); + + if (config->check_data) { + //compare until all received data are used + int compared_len = 0; + do { + //get the expected master sent data, there may be several segments, so get and compare + //several times + uint8_t* cmp_start; + int seg_len; + get_master_send_data(offset, &cmp_start, &seg_len); + + int cmp_len = MIN(rcv_len-compared_len, seg_len); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(cmp_start, &buf[compared_len], cmp_len); + + compared_len += cmp_len; + offset += cmp_len; + } while (compared_len < rcv_len); + } else { + offset += rcv_len; + } + + err = sdio_slave_recv_load_buf(buf_handle); + TEST_ESP_OK(err); + } + wait_for_finish(ctx); +} + +static void slave_tx_rx_short_data(void) +{ + esp_err_t err; + for (int i = 0; i < 10; i++) { + err = sdio_slave_send_queue(tx_buffer + 4*i, TEST_RESET_DATA_LEN, (void*)i, portMAX_DELAY); + TEST_ESP_OK(err); + } + + for (int i = 0; i < 10; i++) { + uint8_t* addr; + size_t size; + sdio_slave_buf_handle_t recv_handle; + err = sdio_slave_recv(&recv_handle, &addr, &size, portMAX_DELAY); + TEST_ESP_OK(err); + TEST_ASSERT_EQUAL(TEST_RESET_DATA_LEN, size); + TEST_ASSERT_EQUAL_HEX8_ARRAY(tx_buffer+i*8, addr, size); + } + + for (int i = 0; i < 10; i++) { + void* arg; + err = sdio_slave_send_get_finished(&arg, portMAX_DELAY); + TEST_ESP_OK(err); + TEST_ASSERT_EQUAL(i, arg); + } +} + +void test_reset_slave(slave_context_t *context, const sdio_test_config_t* config) +{ + sdio_slave_stop(); + + esp_err_t err = sdio_slave_reset(); + TEST_ESP_OK(err); + + err = sdio_slave_start(); + TEST_ESP_OK(err); + + init_tx_buffer(); + + slave_parepare_recv_buffer(); + + slave_tx_rx_short_data(); + + wait_for_finish(context); +} + +void test_sdio_reset_slave(void) +{ + test_framework_slave(test_reset_slave, &packet_config); +} + + +TEST_CASE_MULTIPLE_DEVICES("sdio interrupt", "[sdio][test_env=UT_SDIO]", test_sdio_interrupt_master, test_sdio_interrupt_slave); + +TEST_CASE_MULTIPLE_DEVICES("sdio register", "[sdio][test_env=UT_SDIO]", test_sdio_reg_master, test_sdio_interrupt_slave); + +TEST_CASE_MULTIPLE_DEVICES("sdio reset", "[sdio][test_env=UT_SDIO]", test_sdio_reset_master, test_sdio_reset_slave); + + + +static void test_sdio_frhost_master(const void* pset, void* context) +{ + test_framework_master(test_tp_frhost_master, pset); +} + +static void test_sdio_frhost_slave(const void* pset, void* context) +{ + test_framework_slave(test_tp_frhost_slave, pset); +} + +static void test_sdio_tohost_master(const void* pset, void* context) +{ + test_framework_master(test_tp_tohost_master, pset); +} + +static void test_sdio_tohost_slave(const void* pset, void* context) +{ + test_framework_slave(test_tp_tohost_slave, pset); +} + + +static void null_pre(void** arg) +{ + +} + +static void null_post(void* arg) +{ + +} + +ptest_func_t frhost_master = { + .pre_test = null_pre, + .loop = test_sdio_frhost_master, + .post_test = null_post, +}; + +ptest_func_t frhost_slave = { + .pre_test = null_pre, + .loop = test_sdio_frhost_slave, + .post_test = null_post, +}; + +PARAM_GROUP_DECLARE_TYPE(IO_MODE, sdio_test_config_t, test_cfg_array); + +TEST_MASTER_SLAVE(FRHOST, test_cfg_array, "[sdio][timeout=180][test_env=UT_SDIO]", &frhost_master, &frhost_slave); + +ptest_func_t tohost_master = { + .pre_test = null_pre, + .loop = test_sdio_tohost_master, + .post_test = null_post, +}; + +ptest_func_t tohost_slave = { + .pre_test = null_pre, + .loop = test_sdio_tohost_slave, + .post_test = null_post, +}; + +TEST_MASTER_SLAVE(TOHOST, test_cfg_array, "[sdio][timeout=180][test_env=UT_SDIO]", &tohost_master, &tohost_slave); \ No newline at end of file diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index 1a06c99059..24971ca490 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -35,6 +35,9 @@ #if __has_include("esp_ping.h") #include "esp_ping.h" #endif +#if __has_include("esp_serial_slave_link/essl.h") +#include "esp_serial_slave_link/essl.h" +#endif #if __has_include("esp_spi_flash.h") #include "esp_spi_flash.h" #endif @@ -105,6 +108,10 @@ static const esp_err_msg_t esp_err_msg_table[] = { # endif # ifdef ESP_ERR_INVALID_MAC ERR_TBL_IT(ESP_ERR_INVALID_MAC), /* 267 0x10b MAC address was invalid */ +# endif + // components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h +# ifdef ESP_ERR_NOT_FINISHED + ERR_TBL_IT(ESP_ERR_NOT_FINISHED), /* 513 0x201 */ # endif // components/nvs_flash/include/nvs.h # ifdef ESP_ERR_NVS_BASE diff --git a/components/esp_serial_slave_link/CMakeLists.txt b/components/esp_serial_slave_link/CMakeLists.txt new file mode 100644 index 0000000000..32ccb89077 --- /dev/null +++ b/components/esp_serial_slave_link/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRCS "essl.c" + "essl_sdio.c" + INCLUDE_DIRS "include" + REQUIRES "sdmmc" + "driver" + PRIV_INCLUDE_DIRS "." + "include/esp_serial_slave_link" +) + diff --git a/examples/peripherals/sdio/host/components/esp_slave/component.mk b/components/esp_serial_slave_link/component.mk similarity index 50% rename from examples/peripherals/sdio/host/components/esp_slave/component.mk rename to components/esp_serial_slave_link/component.mk index 5b1ef9e204..a006c287d3 100644 --- a/examples/peripherals/sdio/host/components/esp_slave/component.mk +++ b/components/esp_serial_slave_link/component.mk @@ -3,4 +3,5 @@ # COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := . include/esp_serial_slave_link diff --git a/components/esp_serial_slave_link/essl.c b/components/esp_serial_slave_link/essl.c new file mode 100644 index 0000000000..020b65270c --- /dev/null +++ b/components/esp_serial_slave_link/essl.c @@ -0,0 +1,244 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "essl.h" +#include "essl_internal.h" +#include "esp_log.h" +#include "freertos/task.h" + +#define TIME_EXPIRED_SINCE_CORE(start, end, timeout, max) (bool)((end)>=(start)? \ + ((end)-(start)>(timeout)) :\ + ((max)-(timeout)>(start)-(end))) + +#define TIME_EXPIRED_SINCE(start, end, timeout) TIME_EXPIRED_SINCE_CORE(start, end, timeout, UINT32_MAX) + +#define MINUS_UNTIL_ZERO(a, b) ( ((a) > (b)) ? ((a)-(b)): 0) + +#define TIME_REMAIN_CORE(start, end, timeout, max) ((end)>=(start)?\ + MINUS_UNTIL_ZERO(timeout, (end)-(start)):\ + MINUS_UNTIL_ZERO((start)-(end), (max)-(timeout))) + +#define TIME_REMAIN(start, end, timeout) TIME_REMAIN_CORE(start, end, timeout, UINT32_MAX) + + +#define ESSL_MIN(a, b) ((a) < (b) ? (a) : (b)) + +__attribute__((unused)) static const char TAG[] = "esp_serial_slave_link"; + +#define _CHECK_EXECUTE_CMD(DEV, CMD, STR, ...) do{ \ + if ((DEV) == NULL) { \ + return ESP_ERR_INVALID_ARG; \ + } \ + if ((DEV)->CMD) { \ + return (DEV)->CMD((DEV)->args,##__VA_ARGS__); \ + } else { \ + ESP_LOGE(TAG, STR); \ + return ESP_ERR_NOT_SUPPORTED; \ + } } while(0) + +#define CHECK_EXECUTE_CMD(DEV, CMD, ...) _CHECK_EXECUTE_CMD(DEV, CMD, #CMD" not supported for the current device.",##__VA_ARGS__) + + +esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, init, wait_ms); +} + +esp_err_t essl_wait_for_ready(essl_handle_t handle, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, wait_for_ready, wait_ms); +} + +esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t length, uint32_t wait_ms) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (start == NULL || length == 0) { + return ESP_ERR_INVALID_ARG; + } + if (handle->send_packet == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + esp_err_t err; + const uint32_t timeout_ticks = pdMS_TO_TICKS(wait_ms); + + uint32_t pre = xTaskGetTickCount(); + uint32_t now; + uint32_t remain_wait_ms = 0; + + do { + now = xTaskGetTickCount(); + remain_wait_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->send_packet(handle->args, start, length, remain_wait_ms); + if (err == ESP_OK) { + break; + } else if (err != ESP_ERR_NOT_FOUND) { + return err; + } // else ESP_ERR_NOT_FOUND + //the slave has no enough memory, retry + } while (remain_wait_ms > 0); + return ESP_OK; +} + +esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, size_t *out_length, uint32_t wait_ms) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (out_data == NULL || size == 0 || out_length == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->get_packet == NULL || handle->update_rx_data_size == NULL || handle->get_rx_data_size == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + esp_err_t err; + const uint32_t timeout_ticks = pdMS_TO_TICKS(wait_ms); + + uint32_t pre = xTaskGetTickCount(); + uint32_t now = 3; + uint32_t wait_remain_ms = 0; + int data_available = handle->get_rx_data_size(handle->args); + + // if there is already enough data to read, skip the length update. + if (data_available < size) { + //loop until timeout, or there is at least one byte + do { + now = xTaskGetTickCount(); + wait_remain_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->update_rx_data_size(handle->args, wait_remain_ms); + if (err != ESP_OK) { + return err; + } + data_available = handle->get_rx_data_size(handle->args); + if (data_available > 0) { + break; + } + } while (wait_remain_ms > 0); + } + + if (data_available == 0) { + //the slave has no data to send + return ESP_ERR_NOT_FOUND; + } + + int len = ESSL_MIN(data_available, size); + now = xTaskGetTickCount(); + wait_remain_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->get_packet(handle->args, out_data, len, wait_remain_ms); + if (err != ESP_OK) { + return err; + } + + *out_length = len; + if (len < data_available) { + return ESP_ERR_NOT_FINISHED; + } + return ESP_OK; +} + +esp_err_t essl_get_tx_buffer_num(essl_handle_t handle, uint32_t *out_tx_num, uint32_t wait_ms) +{ + if (handle == NULL || out_tx_num == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->update_tx_buffer_num == NULL|| handle->get_tx_buffer_num == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = handle->update_tx_buffer_num(handle->args, wait_ms); + if (err != ESP_OK) { + return err; + } + + *out_tx_num = handle->get_tx_buffer_num(handle->args); + return ESP_OK; +} + +esp_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uint32_t wait_ms) +{ + if (handle == NULL || out_rx_size == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->update_rx_data_size == NULL|| handle->get_rx_data_size == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = handle->update_rx_data_size(handle->args, wait_ms); + if (err != ESP_OK) { + return err; + } + + *out_rx_size = handle->get_rx_data_size(handle->args); + return ESP_OK; +} + +esp_err_t essl_write_reg(essl_handle_t handle, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, write_reg, addr, value, value_o, wait_ms); +} + +esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, read_reg, add, value_o, wait_ms); +} + +esp_err_t essl_wait_int(essl_handle_t handle, TickType_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, wait_int, wait_ms); +} + +esp_err_t essl_reset_cnt(essl_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->reset_cnt == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + handle->reset_cnt(handle->args); + return ESP_OK; +} + +esp_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, clear_intr, intr_mask, wait_ms); +} + +esp_err_t essl_get_intr(essl_handle_t handle, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms) +{ + if (intr_raw == NULL && intr_st == NULL) { + return ESP_ERR_INVALID_ARG; + } + CHECK_EXECUTE_CMD(handle, get_intr, intr_raw, intr_st, wait_ms); +} + +esp_err_t essl_set_intr_ena(essl_handle_t handle, uint32_t ena_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, set_intr_ena, ena_mask, wait_ms); +} + +esp_err_t essl_get_intr_ena(essl_handle_t handle, uint32_t *ena_mask_o, uint32_t wait_ms) +{ + if (ena_mask_o == NULL) { + return ESP_ERR_INVALID_ARG; + } + CHECK_EXECUTE_CMD(handle, get_intr_ena, ena_mask_o, wait_ms); +} + +esp_err_t essl_send_slave_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, send_slave_intr, intr_mask, wait_ms); +} + diff --git a/components/esp_serial_slave_link/essl_internal.h b/components/esp_serial_slave_link/essl_internal.h new file mode 100644 index 0000000000..0070e53212 --- /dev/null +++ b/components/esp_serial_slave_link/essl_internal.h @@ -0,0 +1,45 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include + +/** Context used by the ``esp_serial_slave_link`` component. + */ +struct essl_dev_t { + void* args; + + esp_err_t (*init)(void* ctx, uint32_t wait_ms); + + esp_err_t (*wait_for_ready)(void *ctx, uint32_t wait_ms); + esp_err_t (*update_tx_buffer_num)(void *ctx, uint32_t wait_ms); + esp_err_t (*update_rx_data_size)(void *ctx, uint32_t wait_ms); + esp_err_t (*send_packet)(void *ctx, const void* start, size_t length, uint32_t wait_ms); + esp_err_t (*get_packet)(void *ctx, void* out_data, size_t size, uint32_t wait_ms); + esp_err_t (*write_reg)(void *ctx, uint8_t addr, uint8_t value, uint8_t* value_o, uint32_t wait_ms); + esp_err_t (*read_reg)(void *ctx, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + esp_err_t (*wait_int)(void *ctx, uint32_t wait_ms); + esp_err_t (*clear_intr)(void* ctx, uint32_t intr_mask, uint32_t wait_ms); + esp_err_t (*get_intr)(void* ctx, uint32_t* intr_raw, uint32_t *intr_st, uint32_t wait_ms); + esp_err_t (*set_intr_ena)(void* ctx, uint32_t ena_mask, uint32_t wait_ms); + esp_err_t (*get_intr_ena)(void* ctx, uint32_t* ena_mask_o, uint32_t wait_ms); + esp_err_t (*send_slave_intr)(void* ctx, uint32_t intr_mask, uint32_t wait_ms); + + uint32_t (*get_tx_buffer_num)(void *ctx); + uint32_t (*get_rx_data_size)(void *ctx); + void (*reset_cnt)(void *ctx); +}; + +typedef struct essl_dev_t essl_dev_t; diff --git a/components/esp_serial_slave_link/essl_sdio.c b/components/esp_serial_slave_link/essl_sdio.c new file mode 100644 index 0000000000..b1e69d352b --- /dev/null +++ b/components/esp_serial_slave_link/essl_sdio.c @@ -0,0 +1,455 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "essl_sdio.h" +#include "esp_log.h" +#include "soc/host_reg.h" +#include "freertos/task.h" +#include "essl_internal.h" + +static const char TAG[] = "essl_sdio"; + +#define ESSL_CMD53_END_ADDR 0x1f800 + +#define TX_BUFFER_MAX 0x1000 +#define TX_BUFFER_MASK 0xFFF +#define RX_BYTE_MAX 0x100000 +#define RX_BYTE_MASK 0xFFFFF + +#define FUNC1_EN_MASK (BIT(1)) + +/** + * Initialize ``void`` over SDIO by this macro. + */ +#define ESSL_SDIO_DEFAULT_CONTEXT() (essl_dev_t){\ + .init = essl_sdio_init, \ + .wait_for_ready = essl_sdio_wait_for_ready, \ + .get_tx_buffer_num = essl_sdio_get_tx_buffer_num,\ + .update_tx_buffer_num = essl_sdio_update_tx_buffer_num,\ + .get_rx_data_size = essl_sdio_get_rx_data_size,\ + .update_rx_data_size = essl_sdio_update_rx_data_size,\ + .send_packet = essl_sdio_send_packet,\ + .get_packet = essl_sdio_get_packet,\ + .write_reg = essl_sdio_write_reg,\ + .read_reg = essl_sdio_read_reg,\ + .wait_int = essl_sdio_wait_int,\ + .send_slave_intr = essl_sdio_send_slave_intr, \ + .get_intr = essl_sdio_get_intr, \ + .clear_intr = essl_sdio_clear_intr, \ + .set_intr_ena = essl_sdio_set_intr_ena, \ + .reset_cnt = essl_sdio_reset_cnt, \ + } + +typedef struct{ + //common part + uint16_t buffer_size; + ///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. + ///< Buffer size of the slave pre-defined between host and slave before communication. + size_t tx_sent_buffers; ///< Counter holding the amount of buffers already sent to ESP32 slave. Should be set to 0 when initialization. + size_t tx_sent_buffers_latest; ///< The latest reading (from the slave) of counter holding the amount of buffers loaded. Should be set to 0 when initialization. + size_t rx_got_bytes; ///< Counter holding the amount of bytes already received from ESP32 slave. Should be set to 0 when initialization. + size_t rx_got_bytes_latest; ///< The latest reading (from the slave) of counter holding the amount of bytes to send. Should be set to 0 when initialization. + + sdmmc_card_t* card; ///< Initialized sdmmc_cmd card + uint16_t block_size; + ///< If this is too large, it takes time to send stuff bits; while if too small, intervals between blocks cost much. + ///< Should be set according to length of data, and larger than ``TRANS_LEN_MAX/511``. + ///< Block size of the SDIO function 1. After the initialization this will hold the value the slave really do. Valid value is 1-2048. +} essl_sdio_context_t; + + +esp_err_t essl_sdio_update_tx_buffer_num(void *arg, uint32_t wait_ms); +esp_err_t essl_sdio_update_rx_data_size(void *arg, uint32_t wait_ms); + +static inline esp_err_t essl_sdio_write_byte(sdmmc_card_t *card, uint32_t addr, uint8_t val, uint8_t *val_o) +{ + return sdmmc_io_write_byte(card, 1, addr&0x3FF, val, val_o); +} + +static inline esp_err_t essl_sdio_write_bytes(sdmmc_card_t *card, uint32_t addr, uint8_t *val, int len) +{ + return sdmmc_io_write_bytes(card, 1, addr&0x3FF, val, len); +} + +static inline esp_err_t essl_sdio_read_byte(sdmmc_card_t *card, uint32_t addr, uint8_t *val_o) +{ + return sdmmc_io_read_byte(card, 1, addr&0x3FF, val_o); +} + +static inline esp_err_t essl_sdio_read_bytes(sdmmc_card_t *card, uint32_t addr, uint8_t *val_o, int len) +{ + return sdmmc_io_read_bytes(card, 1, addr&0x3FF, val_o, len); +} + +esp_err_t essl_sdio_init_dev(essl_handle_t *out_handle, const essl_sdio_config_t *config) +{ + esp_err_t ret = ESP_OK; + essl_sdio_context_t* arg = NULL; + essl_dev_t* dev = NULL; + arg = (essl_sdio_context_t*)heap_caps_malloc(sizeof(essl_sdio_context_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + dev = (essl_dev_t*)heap_caps_malloc(sizeof(essl_dev_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + + if (arg == NULL || dev == NULL) { + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + + *dev = ESSL_SDIO_DEFAULT_CONTEXT(); + dev->args = arg; + + *arg = (essl_sdio_context_t) { + .card = config->card, + .block_size = 0x200, + .buffer_size = config->recv_buffer_size, + .tx_sent_buffers = 0, + .rx_got_bytes = 0, + }; + + *out_handle = dev; + return ESP_OK; + +cleanup: + free(arg); + free(dev); + return ret; +} + +esp_err_t essl_sdio_deinit_dev(essl_handle_t handle) +{ + if (handle) free (handle->args); + free(handle); + return ESP_OK; +} + +esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t err; + uint8_t ioe; + sdmmc_card_t* card = ctx->card; + + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOE: 0x%02x", ioe); + + uint8_t ior = 0; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + + // enable function 1 + ioe |= FUNC1_EN_MASK; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_FN_ENABLE, ioe, &ioe); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOE: 0x%02x", ioe); + + // wait for the card to become ready + while ((ior & FUNC1_EN_MASK) == 0) { + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + } + + // get interrupt status + uint8_t ie; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie); + if (err != ESP_OK) return err; + ESP_LOGD(TAG,"IE: 0x%02x", ie); + + // enable interrupts for function 1&2 and master enable + ie |= BIT(0) | FUNC1_EN_MASK; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_INT_ENABLE, ie, &ie); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IE: 0x%02x", ie); + + // get bus width register + uint8_t bus_width; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, &bus_width); + if (err != ESP_OK) return err; + ESP_LOGD(TAG,"BUS_WIDTH: 0x%02x", bus_width); + + // enable continuous SPI interrupts + bus_width |= CCCR_BUS_WIDTH_ECSI; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, bus_width, &bus_width); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "BUS_WIDTH: 0x%02x", bus_width); + + uint16_t bs = 512; + const uint8_t* bs_u8 = (const uint8_t*) &bs; + uint16_t bs_read = 0; + uint8_t* bs_read_u8 = (uint8_t*) &bs_read; + // Set block sizes for functions 0 to 512 bytes + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 0 BS: %04x", (int) bs_read); + + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 0 BS: %04x", (int) bs_read); + + // Set block sizes for functions 1 to given value (default value = 512). + if (ctx->block_size > 0 || ctx->block_size <= 2048) { + bs = ctx->block_size; + } else { + bs = 512; + } + size_t offset = SD_IO_FBR_START * 1; + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 1 BS: %04x", (int) bs_read); + + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 1 BS: %04x", (int) bs_read); + + if (bs_read != ctx->block_size) { + ESP_LOGW(TAG, "Function1 block size %d different than set value %d", bs_read, ctx->block_size); + ctx->block_size = bs_read; + } + return ESP_OK; +} + +esp_err_t essl_sdio_wait_for_ready(void *arg, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "wait_for_ioready"); + esp_err_t err; + sdmmc_card_t *card = ((essl_sdio_context_t*)arg)->card; + // wait for the card to become ready + uint8_t ior = 0; + while ((ior & FUNC1_EN_MASK) == 0) { + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + } + return ESP_OK; +} + +esp_err_t essl_sdio_send_packet(void *arg, const void *start, size_t length, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint16_t buffer_size = ctx->buffer_size; + int buffer_used = (length + buffer_size - 1)/buffer_size; + esp_err_t err; + + if (essl_sdio_get_tx_buffer_num(arg) < buffer_used) { + //slave has no enough buffer, try update for once + esp_err_t err = essl_sdio_update_tx_buffer_num(arg, wait_ms); + if (err != ESP_OK) { + return err; + } + if (essl_sdio_get_tx_buffer_num(arg) < buffer_used) { + ESP_LOGV(TAG, "buffer is not enough: %d, %d required.", ctx->tx_sent_buffers_latest, ctx->tx_sent_buffers + buffer_used); + return ESP_ERR_NOT_FOUND; + } + } + + ESP_LOGV(TAG, "send_packet: len: %d", length); + + uint8_t *start_ptr = (uint8_t*)start; + uint32_t len_remain = length; + do { + const int block_size = 512; + /* Though the driver supports to split packet of unaligned size into + * length of 4x and 1~3, we still send aligned size of data to get + * higher effeciency. The length is determined by the SDIO address, and + * the remainning will be discard by the slave hardware. + */ + int block_n = len_remain/block_size; + int len_to_send; + if (block_n) { + len_to_send = block_n * block_size; + err = sdmmc_io_write_blocks(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start_ptr, len_to_send); + } else { + len_to_send = len_remain; + err = sdmmc_io_write_bytes(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start_ptr, (len_to_send + 3) & (~3)); + } + if (err != ESP_OK) return err; + start_ptr += len_to_send; + len_remain -= len_to_send; + } while (len_remain); + + ctx->tx_sent_buffers += buffer_used; + return ESP_OK; +} + +esp_err_t essl_sdio_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t err; + + ESP_LOGV(TAG, "get_packet: read size=%d", size); + if (essl_sdio_get_rx_data_size(arg) < size) { + err = essl_sdio_update_rx_data_size(arg, wait_ms); + if (err != ESP_OK) { + return err; + } + if (essl_sdio_get_rx_data_size(arg) < size) { + return ESP_ERR_NOT_FOUND; + } + } + + uint8_t *start = out_data; + uint32_t len_remain = size; + do { + const int block_size = 512; //currently our driver don't support block size other than 512 + int len_to_send; + + int block_n = len_remain/block_size; + if (block_n != 0) { + len_to_send = block_n * block_size; + err = sdmmc_io_read_blocks(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start, len_to_send); + } else { + len_to_send = len_remain; + /* though the driver supports to split packet of unaligned size into length + * of 4x and 1~3, we still get aligned size of data to get higher + * effeciency. The length is determined by the SDIO address, and the + * remainning will be ignored by the slave hardware. + */ + err = sdmmc_io_read_bytes(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start, (len_to_send + 3) & (~3)); + } + if (err != ESP_OK) return err; + start += len_to_send; + len_remain -= len_to_send; + ctx->rx_got_bytes += len_to_send; + } while(len_remain!=0); + + return err; +} + +uint32_t essl_sdio_get_tx_buffer_num(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ESP_LOGV(TAG, "tx latest: %d, sent: %d", ctx->tx_sent_buffers_latest, ctx->tx_sent_buffers); + return (ctx->tx_sent_buffers_latest + TX_BUFFER_MAX - ctx->tx_sent_buffers)%TX_BUFFER_MAX; +} + +esp_err_t essl_sdio_update_tx_buffer_num(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint32_t len; + esp_err_t err; + + err = essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_TOKEN_RDATA_REG, (uint8_t *) &len, 4); + if (err != ESP_OK) return err; + len = (len>>16)&TX_BUFFER_MASK; + ctx->tx_sent_buffers_latest = len; + ESP_LOGV(TAG, "update_tx_buffer_num: %d", len); + return ESP_OK; +} + +uint32_t essl_sdio_get_rx_data_size(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ESP_LOGV(TAG, "rx latest: %d, read: %d", ctx->rx_got_bytes_latest, ctx->rx_got_bytes); + return (ctx->rx_got_bytes_latest + RX_BYTE_MAX - ctx->rx_got_bytes)%RX_BYTE_MAX; +} + +esp_err_t essl_sdio_update_rx_data_size(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint32_t len; + esp_err_t err; + + ESP_LOGV(TAG, "get_rx_data_size: got_bytes: %d", ctx->rx_got_bytes); + err = essl_sdio_read_bytes(ctx->card, HOST_SLCHOST_PKT_LEN_REG, (uint8_t *) &len, 4); + if (err != ESP_OK) return err; + len &= RX_BYTE_MASK; + ctx->rx_got_bytes_latest = len; + return ESP_OK; +} + + +esp_err_t essl_sdio_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "write_reg: %08X", value); + // addrress over range + if (addr >= 60) return ESP_ERR_INVALID_ARG; + //W7 is reserved for interrupts + if (addr >= 28) addr += 4; + return essl_sdio_write_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W_REG(addr), value, value_o); +} + +esp_err_t essl_sdio_read_reg(void *arg, uint8_t add, uint8_t *value_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "read_reg"); + // address over range + if (add >= 60) return ESP_ERR_INVALID_ARG; + //W7 is reserved for interrupts + if (add >= 28) add += 4; + esp_err_t ret = essl_sdio_read_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W_REG(add), value_o); + ESP_LOGV(TAG, "reg: %08X", *value_o); + return ret; +} + +esp_err_t essl_sdio_clear_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "clear_intr: %08X", intr_mask); + return essl_sdio_write_bytes(((essl_sdio_context_t *) arg)->card, HOST_SLC0HOST_INT_CLR_REG, (uint8_t *) &intr_mask, 4); +} + +esp_err_t essl_sdio_get_intr(void *arg, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t r; + ESP_LOGV(TAG, "get_intr"); + if (intr_raw == NULL && intr_st == NULL) return ESP_ERR_INVALID_ARG; + + if (intr_raw != NULL) { + r= essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_INT_RAW_REG, (uint8_t *) intr_raw, 4); + if (r != ESP_OK) return r; + } + if (intr_st != NULL) { + r = essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_INT_ST_REG, (uint8_t *) intr_st, 4); + if (r != ESP_OK) return r; + } + return ESP_OK; +} + +esp_err_t essl_sdio_set_intr_ena(void *arg, uint32_t ena_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "set_intr_ena: %08X", ena_mask); + return essl_sdio_write_bytes(((essl_sdio_context_t*)arg)->card, HOST_SLC0HOST_FUNC1_INT_ENA_REG, + (uint8_t *) &ena_mask, 4); +} + +esp_err_t essl_sdio_get_intr_ena(void *arg, uint32_t *ena_mask_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "get_intr_ena"); + esp_err_t ret = essl_sdio_read_bytes(((essl_sdio_context_t*)arg)->card, HOST_SLC0HOST_FUNC1_INT_ENA_REG, + (uint8_t *) ena_mask_o, 4); + ESP_LOGV(TAG, "ena: %08X", *ena_mask_o); + return ret; +} + +esp_err_t essl_sdio_send_slave_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "send_slave_intr: %02x", intr_mask); + return essl_sdio_write_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W7_REG + 0, intr_mask, NULL); +} + +esp_err_t essl_sdio_wait_int(void *arg, uint32_t wait_ms) +{ + return sdmmc_io_wait_int(((essl_sdio_context_t*)arg)->card, wait_ms); +} + +void essl_sdio_reset_cnt(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ctx->rx_got_bytes = 0; + ctx->tx_sent_buffers = 0; +} \ No newline at end of file diff --git a/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h new file mode 100644 index 0000000000..100e86a163 --- /dev/null +++ b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h @@ -0,0 +1,214 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "sdmmc_cmd.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#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; + +/** + * @brief Initialize the slave. + * + * @param handle Handle of a ``essl`` device. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * @return ESP_OK if success, or other value returned from lower layer `init`. + */ +esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms); + +/** Wait for interrupt of a ESP32 slave device. + * + * @param handle Handle of a ``essl`` device. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK if success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_wait_for_ready(essl_handle_t handle, uint32_t wait_ms); + +/** Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``. + * + * @param handle Handle of a ``essl`` device. + * @param out_tx_num Output of buffer num that host can send data to ESP32 slave. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_get_tx_buffer_num(essl_handle_t handle, uint32_t *out_tx_num, uint32_t wait_ms); + +/** Get amount of data the ESP32 slave preparing to send to host. + * + * @param handle Handle of a ``essl`` device. + * @param out_rx_size Output of data size to read from slave. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uint32_t wait_ms); + + +/** Reset the counters of this component. Usually you don't need to do this unless you know the slave is reset. + * + * @param handle Handle of a ``essl`` device. + */ +esp_err_t essl_reset_cnt(essl_handle_t handle); + +/** Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` (configured during initialization). + * + * @param handle Handle of a ``essl`` device. + * @param start Start address of the packet to send + * @param length Length of data to send, if the packet is over-size, the it will be divided into blocks and hold into different buffers automatically. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - ESP_ERR_TIMEOUT No buffer to use, or error ftrom SDMMC host controller + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t length, uint32_t wait_ms); + +/** Get a packet from ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param[out] out_data Data output address + * @param size The size of the output buffer, if the buffer is smaller than the size of data to receive from slave, the driver returns ``ESP_ERR_NOT_FINISHED`` + * @param[out] out_length Output of length the data actually received from slave. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success, all the data are read from the slave. + * - ESP_ERR_NOT_FINISHED Read success, while there're data remaining. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, size_t *out_length, uint32_t wait_ms); + +/** Write general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param addr Address of register to write. Valid address: 0-59. + * @param value Value to write to the register. + * @param value_o Output of the returned written value. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @note sdio 28-31 are reserved, the lower API helps to skip. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_write_reg(essl_handle_t handle, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms); + +/** Read general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). + * @param value_o Output value read from the register. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + +/** wait for an interrupt of the slave + * + * @param handle Handle of a ``essl`` device. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_ERR_NOT_SUPPORTED Currently our driver doesnot support SDIO with SPI interface. + * - ESP_OK If interrupt triggered. + * - ESP_ERR_TIMEOUT No interrupts before timeout. + */ +esp_err_t essl_wait_int(essl_handle_t handle, uint32_t wait_ms); + +/** Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same. + * + * @param handle Handle of a ``essl`` device. + * @param intr_mask Mask of interrupt bits to clear. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms); + +/** Get interrupt bits of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read. + * @param intr_st Output of the masked interrupt bits. set to NULL if only raw bits are read. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - ESP_INVALID_ARG if both ``intr_raw`` and ``intr_st`` are NULL. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_get_intr(essl_handle_t handle, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms); + +/** Set interrupt enable bits of ESP32 slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set. + * + * @param handle Handle of a ``essl`` device. + * @param ena_mask Mask of the interrupt bits to enable. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_set_intr_ena(essl_handle_t handle, uint32_t ena_mask, uint32_t wait_ms); + +/** Get interrupt enable bits of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param ena_mask_o Output of interrupt bit enable mask. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_get_intr_ena(essl_handle_t handle, uint32_t *ena_mask_o, uint32_t wait_ms); + +/** Send interrupts to slave. Each bit of the interrupt will be triggered. + * + * @param handle Handle of a ``essl`` device. + * @param intr_mask Mask of interrupt bits to send to slave. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_send_slave_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms); + + diff --git a/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h new file mode 100644 index 0000000000..93fa2823c2 --- /dev/null +++ b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h @@ -0,0 +1,248 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ESP SDIO slave link used by the ESP host to communicate with ESP32 SDIO slave. + +#pragma once + +#include "esp_err.h" +#include "esp_serial_slave_link/essl.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_defs.h" + +/// Configuration for the essl SDIO device +typedef struct { + sdmmc_card_t *card; ///< The initialized sdmmc card pointer of the slave. + int recv_buffer_size; ///< The pre-negotiated recv buffer size used by both the host and the slave. +} essl_sdio_config_t; + +/** + * @brief Initialize the ESSL SDIO device and get its handle. + * + * @param out_handle Output of the handle. + * @param config Configuration for the ESSL SDIO device. + * @return + * - ESP_OK: on success + * - ESP_ERR_NO_MEM: memory exhausted. + */ +esp_err_t essl_sdio_init_dev(essl_handle_t *out_handle, const essl_sdio_config_t *config); + +/** + * @brief Deinitialize and free the space used by the ESSL SDIO device. + * + * @param handle Handle of the ESSL SDIO device to deinit. + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: wrong handle passed + */ +esp_err_t essl_sdio_deinit_dev(essl_handle_t handle); + +//Please call `essl_` functions witout `sdio` instead of calling these functions directly. +/** @cond */ +/** + * SDIO Initialize process of a ESP32 slave device. + * + * @param arg Context of the ``essl`` component. Send to other functions later. + * @param wait_ms Time to wait before operation is done, in ms. + * + * @return + * - ESP_OK if success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms); + +/** + * Wait for interrupt of a ESP32 slave device. + * + * @param arg Context of the ``essl`` component. + * @param wait_ms Time to wait before operation is done, in ms. + * + * @return + * - ESP_OK if success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_wait_for_ready(void *arg, uint32_t wait_ms); + +/** + * Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``. + * + * @param arg Context of the component. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +uint32_t essl_sdio_get_tx_buffer_num(void *arg); + +/** Get amount of data the ESP32 slave preparing to send to host. + * + * @param arg Context of the component. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +uint32_t essl_sdio_get_rx_data_size(void *arg); + +/** + * Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` in the arg. + * + * @param arg Context of the component. + * @param start Start address of the packet to send + * @param length Length of data to send, if the packet is over-size, the it will be divided into blocks and hold into different buffers automatically. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_ERR_TIMEOUT No buffer to use, or error ftrom SDMMC host controller + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_send_packet(void *arg, const void *start, size_t length, uint32_t wait_ms); + +/** + * Get a packet from ESP32 slave. + * + * @param arg Context of the component. + * @param[out] out_data Data output address + * @param size The size of the output buffer, if the buffer is smaller than the size of data to receive from slave, the driver returns ``ESP_ERR_NOT_FINISHED`` + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success, all the data are read from the slave. + * - ESP_ERR_NOT_FINISHED Read success, while there're data remaining. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms); + +/** + * Wait for the interrupt from the SDIO slave. + * + * @param arg Context of the component. + * @param wait_ms Time to wait before timeout, in ms. + * @return + * - ESP_ERR_NOT_SUPPORTED: if the interrupt line is not initialized properly. + * - ESP_OK: if interrupt happened + * - ESP_ERR_TIMEOUT: if timeout before interrupt happened. + * - or other values returned from the `io_int_wait` member of the `card->host` structure. + */ +esp_err_t essl_sdio_wait_int(void *arg, uint32_t wait_ms); + +/** + * Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same. + * + * @param arg Context of the component. + * @param intr_mask Mask of interrupt bits to clear. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_clear_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms); + +/** + * Get interrupt bits of ESP32 slave. + * + * @param arg Context of the component. + * @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read. + * @param intr_st Output of the masked interrupt bits. set to NULL if only raw bits are read. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_INVALID_ARG if both ``intr_raw`` and ``intr_st`` are NULL. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_get_intr(void *arg, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms); + +/** + * Set interrupt enable bits of ESP32 slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set. + * + * @param arg Context of the component. + * @param ena_mask Mask of the interrupt bits to enable. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_set_intr_ena(void *arg, uint32_t ena_mask, uint32_t wait_ms); + +/** + * Get interrupt enable bits of ESP32 slave. + * + * @param arg Context of the component. + * @param ena_mask_o Output of interrupt bit enable mask. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_get_intr_ena(void *arg, uint32_t *ena_mask_o, uint32_t wait_ms); + +/** + * Write general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param arg Context of the component. + * @param addr Address of register to write. Valid address: 0-27, 32-63 (28-31 reserved). + * @param value Value to write to the register. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms); + +/** + * Read general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param arg Context of the component. + * @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). + * @param value Output value read from the register. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_read_reg(void *arg, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + +/** + * Send interrupts to slave. Each bit of the interrupt will be triggered. + * + * @param arg Context of the component. + * @param intr_mask Mask of interrupt bits to send to slave. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_send_slave_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms); + +/** + * @brief Reset the counter on the host side. + * + * @note Only call when you know the slave has reset its counter, or there will be inconsistent between the master and the slave. + * + * @param arg Context of the component. + */ +void essl_sdio_reset_cnt(void *arg); + +/** @endcond */ diff --git a/components/freertos/include/freertos/projdefs.h b/components/freertos/include/freertos/projdefs.h index 1bd39c58d8..edb5007303 100644 --- a/components/freertos/include/freertos/projdefs.h +++ b/components/freertos/include/freertos/projdefs.h @@ -78,6 +78,7 @@ typedef void (*TaskFunction_t)( void * ); /* Converts a time in milliseconds to a time in ticks. */ #define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) +#define pdTICKS_TO_MS( xTicks ) ( ( uint32_t ) ( xTicks ) * 1000 / configTICK_RATE_HZ ) #define pdFALSE ( ( BaseType_t ) 0 ) #define pdTRUE ( ( BaseType_t ) 1 ) diff --git a/components/idf_test/include/idf_performance.h b/components/idf_test/include/idf_performance.h index 63b42076d4..ff8654c0b5 100644 --- a/components/idf_test/include/idf_performance.h +++ b/components/idf_test/include/idf_performance.h @@ -37,3 +37,11 @@ #define IDF_PERFORMANCE_MAX_RSA_2048KEY_PRIVATE_OP 180000 #define IDF_PERFORMANCE_MAX_RSA_4096KEY_PUBLIC_OP 65000 #define IDF_PERFORMANCE_MAX_RSA_4096KEY_PRIVATE_OP 850000 + +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_4BIT 13000 +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_4BIT 13000 +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_1BIT 4000 +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_1BIT 4000 +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_SPI 1000 +#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_SPI 1000 + diff --git a/docs/Doxyfile b/docs/Doxyfile index 72b101fc6b..497ce96afb 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -145,6 +145,9 @@ INPUT = \ ../../components/esp_https_server/include/esp_https_server.h \ ## ESP Local Ctrl ../../components/esp_local_ctrl/include/esp_local_ctrl.h \ + ## ESP Serial Slave Link + ../../components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h \ + ../../components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h \ ## ## Provisioning - API Reference ## diff --git a/docs/en/api-reference/peripherals/esp_slave_protocol.rst b/docs/en/api-reference/peripherals/esp_slave_protocol.rst index 37fd21912e..e99f7830ab 100644 --- a/docs/en/api-reference/peripherals/esp_slave_protocol.rst +++ b/docs/en/api-reference/peripherals/esp_slave_protocol.rst @@ -76,7 +76,10 @@ ESP SDIO slave protocol ----------------------- The protocol is based on Function 1 access by CMD52 and CMD53, offering 3 services: (1) sending and receiving FIFO, (2) 52 8-bit R/W -register shared by host and slave, (3) 8 general purpose interrupt sources from host to slave and 8 in the oppsite direction. +register shared by host and slave, (3) 8 general purpose interrupt sources from host to slave and 8 in the opposite direction. + +There is a component `esp_serial_slave_link` implementing the logic of this protocol for +ESP32 master to communicate with the ESP32 slave. See :doc:`/api-reference/protocols/esp_serial_slave_link`. The host should access the registers below as described to communicate with slave. @@ -154,7 +157,7 @@ To write the receiving FIFO in the slave, host should work in the following step 1. Read the TOKEN1 field (bits 27-16) of TOKEN_RDATA (0x044) register. The buffer number remaining is TOKEN1 minus the number of buffers used by host. -2. Make sure the buffer number is sufficient (*buffer_size* * *buffer_num* is greater than data to write, *buffer_size* +2. Make sure the buffer number is sufficient (*recv_buffer_size* * *buffer_num* is greater than data to write, *recv_buffer_size* is pre-defined between the host and the slave before the communication starts). Or go back to step 1 until the buffer is enough. 3. Write to the FIFO address with CMD53. Note that the *requested length* should not be larger than calculated in step 2, diff --git a/docs/en/api-reference/peripherals/sdio_slave.rst b/docs/en/api-reference/peripherals/sdio_slave.rst index d24b8a9be0..d7473be27e 100644 --- a/docs/en/api-reference/peripherals/sdio_slave.rst +++ b/docs/en/api-reference/peripherals/sdio_slave.rst @@ -127,9 +127,11 @@ SDIO initialization process (Sector 3.1.2 of `SDIO Simplified Specification `_), which is described briefly in :ref:`esp_slave_init`. -Furthermore, there's an ESP32-specific upper-level communication protocol upon -the CMD52/CMD53 to Func 1. Please refer to :ref:`esp_slave_protocol_layer`, -or example :example:`peripherals/sdio` when programming your host. +Furthermore, there's an ESP32-specific upper-level communication protocol upon the CMD52/CMD53 to +Func 1. Please refer to :ref:`esp_slave_protocol_layer`. There is also a component +:doc:`ESP Serial Slave Link ` +for ESP32 master to communicate with ESP32 SDIO slave, see example :example:`peripherals/sdio` +when programming your host. .. toctree:: :hidden: diff --git a/docs/en/api-reference/protocols/esp_serial_slave_link.rst b/docs/en/api-reference/protocols/esp_serial_slave_link.rst new file mode 100644 index 0000000000..90c39563b0 --- /dev/null +++ b/docs/en/api-reference/protocols/esp_serial_slave_link.rst @@ -0,0 +1,154 @@ +ESP Serial Slave Link +===================== + +Overview +-------- + +Espressif provides several chips that can work as slaves. These slave devices rely on some +common buses, and have their own communication protocols over those buses. The `esp_serial_slave_link` component is +designed for the master to communicate with ESP slave devices through those protocols over the +bus drivers. + +After an `esp_serial_slave_link` device is initialized properly, the application can use it to communicate with the ESP +slave devices conveniently. + +For more details about ESP32 SDIO slave protocol, see document :doc:`/api-reference/peripherals/esp_slave_protocol`. + +Terminology +----------- + +- ESSL: Abbreviation for ESP Serial Slave Link, the component described by this document. + +- Master: The device running the `esp_serial_slave_link` component. + +- ESSL device: a virtual device on the master associated with an ESP slave device. The device + context has the knowledge of the slave protocol above the bus, relying on some bus drivers to + communicate with the slave. + +- ESSL device handle: a handle to ESSL device context containing the configuration, status and + data required by the ESSL component. The context stores the driver configurations, + communication state, data shared by master and slave, etc. + + The context should be initialized before it is used, and get deinitialized if not used any more. The + master application operates on the ESSL device through this handle. + +- ESP slave: the slave device connected to the bus, which ESSL component is designed to + communicate with. + +- Bus: The bus over which the master and the slave communicate with each other. + +- Slave protocol: The special communication protocol specified by Espressif HW/SW over the bus. + +- TX buffer num: a counter, which is on the slave and can be read by the master, indicates the + accumulated buffer numbers that the slave has loaded to the hardware to receive data from the + master. + +- RX data size: a counter, which is on the slave and can be read by the master, indicates the + accumulated data size that the slave has loaded to the hardware to send to the master. + +Services provided by ESP slave +------------------------------ + +There are some common services provided by the Espressif slaves: + +1. Tohost Interrupts: The slave can inform the master about certain events by the interrupt line. + +2. Frhost Interrupts: The master can inform the slave about certain events. + +3. Tx FIFO (master to slave): the slave can send data in stream to the master. The SDIO slave can + also indicate it has new data to send to master by the interrupt line. + + The slave updates the TX buffer num to inform the master how much data it can receive, and the + master then read the TX buffer num, and take off the used buffer number to know how many buffers are remaining. + +4. Rx FIFO (slave to master): the slave can receive data from the master in units of receiving + buffers. + + The slave updates the RX data size to inform the master how much data it has prepared to + send, and then the master read the data size, and take off the data length it has already received to know how many + data is remaining. + +5. Shared registers: the master can read some part of the registers on the slave, and also write + these registers to let the slave read. + + +Initialization of ESP SDIO Slave Link +------------------------------------- + +The ESP SDIO slave link (ESSL SDIO) devices relies on the sdmmc component. The ESSL device should +be initialized as below: + +1. Initialize a sdmmc card (see :doc:` Document of SDMMC driver `) + structure. + +2. Call :cpp:func:`sdmmc_card_init` to initialize the card. + +3. Initialize the ESSL device with :cpp:type:`essl_sdio_config_t`. The `card` member should be + the :cpp:type:`sdmmc_card_t` got in step 2, and the `recv_buffer_size` member should be filled + correctly according to pre-negotiated value. + +4. Call :cpp:func:`essl_init` to do initialization of the SDIO part. + +5. Call :cpp:func:`essl_wait_for_ready` to wait for the slave to be ready. + +APIs +---- + +After the initialization process above is performed, you can call the APIs below to make use of +the services provided by the slave: + +Interrupts +^^^^^^^^^^ + +1. Call :cpp:func:`essl_get_intr_ena` to know which events will trigger the interrupts to the master. + +2. Call :cpp:func:`essl_set_intr_ena` to set the events that will trigger interrupts to the master. + +3. Call :cpp:func:`essl_wait_int` to wait until interrupt from the slave, or timeout. + +4. When interrupt is triggered, call :cpp:func:`essl_get_intr` to know which events are active, + and call :cpp:func:`essl_clear_intr` to clear them. + +5. Call :cpp:func:`essl_send_slave_intr` to trigger general purpose interrupt of the slave. + +TX FIFO +^^^^^^^ + +1. Call :cpp:func:`essl_get_tx_buffer_num` to know how many buffers the slave has prepared to + receive data from the master. This is optional. The master will poll `tx_buffer_num` when it try + to send packets to the slave, until the slave has enough buffer or timeout. + +2. Call :cpp:func:`essl_send_paket` to send data to the slave. + +RX FIFO +^^^^^^^ + +1. Call :cpp:func:`essl_get_rx_data_size` to know how many data the slave has prepared to send to + the master. This is optional. When the master tries to receive data from the slave, it will update + the `rx_data_size` for once, if the current `rx_data_size` is shorter than the buffer size the + master prepared to receive. And it may poll the `rx_data_size` if the `rx_dat_size` keeps 0, + until timeout. + +2. Call :cpp:func:`essl_get_packet` to receive data from the slave. + +Reset counters (Optional) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Call :cpp:func:`essl_reset_cnt` to reset the internal counter if you find the slave has reset its +counter. + + +Application Example +------------------- + +The example below shows how ESP32 SDIO host and slave communicate with each other. The host use the ESSL SDIO. + +:example:`peripherals/sdio`. + +Please refer to the specific example README.md for details. + +API Reference +------------- + +.. include:: /_build/inc/essl.inc +.. include:: /_build/inc/essl_sdio.inc \ No newline at end of file diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index 7c5cdd92c3..ded1cbbae1 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -16,6 +16,7 @@ Application Protocols mDNS Modbus Websocket Client + ESP Serial Slave Link Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples. diff --git a/docs/en/api-reference/storage/sdmmc.rst b/docs/en/api-reference/storage/sdmmc.rst index d650ccdaf4..e0a8c38702 100644 --- a/docs/en/api-reference/storage/sdmmc.rst +++ b/docs/en/api-reference/storage/sdmmc.rst @@ -71,7 +71,7 @@ In particular, the driver does not set any bits in (1) I/O Enable and Int Enable For card configuration and data transfer, choose the pair of functions relevant to your case from the table below. ========================================================================= ================================= ================================= -Action Read Function Write Function +Action Read Function Write Function ========================================================================= ================================= ================================= Read and write a single byte using IO_RW_DIRECT (CMD52) :cpp:func:`sdmmc_io_read_byte` :cpp:func:`sdmmc_io_write_byte` Read and write multiple bytes using IO_RW_EXTENDED (CMD53) in byte mode :cpp:func:`sdmmc_io_read_bytes` :cpp:func:`sdmmc_io_write_bytes` @@ -82,6 +82,8 @@ SDIO interrupts can be enabled by the application using the function :cpp:func:` If you want the application to wait until the SDIO interrupt occurs, use :cpp:func:`sdmmc_io_wait_int`. +There is a component ESSL (ESP Serial Slave Link) to use if you are communicating with an ESP32 +SDIO slave. See :doc:`/api-reference/protocols/esp_serial_slave_link` and example :example:`peripherals/sdio/host`. Combo (memory + IO) cards ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst b/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst new file mode 100644 index 0000000000..42578ae599 --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/esp_serial_slave_link.rst \ No newline at end of file diff --git a/docs/zh_CN/api-reference/protocols/index.rst b/docs/zh_CN/api-reference/protocols/index.rst index 8b1e4064fd..66a1b3303c 100644 --- a/docs/zh_CN/api-reference/protocols/index.rst +++ b/docs/zh_CN/api-reference/protocols/index.rst @@ -3,19 +3,20 @@ :link_to_translation:`en:[English]` .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - mDNS - ESP-TLS - HTTP Client - Websocket Client - HTTP 服务器 - HTTPS Server - ICMP Echo - ASIO - ESP-MQTT - Modbus slave - Local Control + mDNS + ESP-TLS + HTTP Client + Websocket Client + HTTP 服务器 + HTTPS Server + ICMP Echo + ASIO + ESP-MQTT + Modbus slave + Local Control + ESP Serial Slave Link 此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。 diff --git a/examples/peripherals/sdio/README.md b/examples/peripherals/sdio/README.md index 737f6c7611..39ef48a16a 100644 --- a/examples/peripherals/sdio/README.md +++ b/examples/peripherals/sdio/README.md @@ -84,7 +84,7 @@ better or disabling the HS mode in menuconfig. to pull down DAT2 line to set proper flash voltage. This conflicts with SDIO pullup requirements. Currently devkits using PICO-D4 and Wroom-32 series modules have this problem. You can either: - + - Use Wrover Kit v3 which integrates a Wrover module - Still use PICO-D4 or Wroom-32 Series modules as the slave, however: - Don't connect the DAT2 pin and leave it floating. This means @@ -101,13 +101,8 @@ and ``api_reference/peripherals/sd_pullup_requirements`` to see more descriptions about pullups and MTDI requirements and solutions of official modules and devkits. -## About esp_slave component in this example +## About `esp_serial_slave_link` component used in this example -The component in this example shows how to communicate with esp32 sdio slave -correctly. However, currently it is for example purpose only. - -The example shows how to talk with the slave, but doesn't show how to handle -exceptions. Assertion fails if any of the preconditions (connections, -grounding, slave data preparation, etc.) is not met. - -Please do check and handle the return value in your real product. +`esp_serial_slave_link` component in the IDF is used to communicate to a ESP slave device. +When the `esp_serial_slave_link` device is initialized with an `essl_sdio_config_t` structure, +the `esp_serial_slave_link` can be used to communicate with an ESP32 SDIO slave. diff --git a/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt b/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt deleted file mode 100644 index 9f5c38bbaf..0000000000 --- a/examples/peripherals/sdio/host/components/esp_slave/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "esp_slave.c" - INCLUDE_DIRS "include" - REQUIRES driver sdmmc) \ No newline at end of file diff --git a/examples/peripherals/sdio/host/components/esp_slave/esp_slave.c b/examples/peripherals/sdio/host/components/esp_slave/esp_slave.c deleted file mode 100644 index fdfaa291e3..0000000000 --- a/examples/peripherals/sdio/host/components/esp_slave/esp_slave.c +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "esp_slave.h" -#include "esp_log.h" -#include "freertos/task.h" -#include "soc/sdio_slave_periph.h" - -static const char TAG[] = "esp_slave"; - -#define ESP_SLAVE_CMD53_END_ADDR 0x1f800 - -#define TX_BUFFER_MAX 0x1000 -#define TX_BUFFER_MASK 0xFFF -#define RX_BYTE_MAX 0x100000 -#define RX_BYTE_MASK 0xFFFFF - -#define FUNC1_EN_MASK (BIT(1)) - -esp_err_t esp_slave_init_io(esp_slave_context_t *context) -{ - esp_err_t err; - uint8_t ioe; - sdmmc_card_t* card = context->card; - - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "IOE: 0x%02x", ioe); - - uint8_t ior = 0; - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "IOR: 0x%02x", ior); - - // enable function 1 - ioe |= FUNC1_EN_MASK; - err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_FN_ENABLE, ioe, &ioe); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "IOE: 0x%02x", ioe); - - // wait for the card to become ready - while ((ior & FUNC1_EN_MASK) == 0) { - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "IOR: 0x%02x", ior); - } - - // get interrupt status - uint8_t ie; - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie); - if (err != ESP_OK) return err; - ESP_LOGD(TAG,"IE: 0x%02x", ie); - - // enable interrupts for function 1&2 and master enable - ie |= BIT(0) | FUNC1_EN_MASK; - err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_INT_ENABLE, ie, &ie); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "IE: 0x%02x", ie); - - // get bus width register - uint8_t bus_width; - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, &bus_width); - if (err != ESP_OK) return err; - ESP_LOGD(TAG,"BUS_WIDTH: 0x%02x", bus_width); - - // enable continuous SPI interrupts - bus_width |= CCCR_BUS_WIDTH_ECSI; - err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, bus_width, &bus_width); - if (err != ESP_OK) return err; - ESP_LOGD(TAG, "BUS_WIDTH: 0x%02x", bus_width); - - uint16_t bs = 512; - const uint8_t* bs_u8 = (const uint8_t*) &bs; - uint16_t bs_read = 0; - uint8_t* bs_read_u8 = (uint8_t*) &bs_read; - // Set block sizes for functions 0 to 512 bytes - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); - ESP_LOGI(TAG, "Function 0 BS: %04x", (int) bs_read); - - ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); - ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); - ESP_LOGI(TAG, "Function 0 BS: %04x", (int) bs_read); - - // Set block sizes for functions 1 to given value (default value = 512). - if (context->block_size > 0 || context->block_size <= 2048) { - bs = context->block_size; - } else { - bs = 512; - } - size_t offset = SD_IO_FBR_START * 1; - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); - ESP_LOGI(TAG, "Function 1 BS: %04x", (int) bs_read); - - ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); - ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); - ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); - ESP_LOGI(TAG, "Function 1 BS: %04x", (int) bs_read); - - if (bs_read != context->block_size) { - ESP_LOGW(TAG, "Function1 block size %d different than set value %d", bs_read, context->block_size); - context->block_size = bs_read; - } - return ESP_OK; -} - -esp_err_t esp_slave_wait_for_ioready(esp_slave_context_t *context) -{ - ESP_LOGV(TAG, "wait_for_ioready"); - esp_err_t err; - sdmmc_card_t *card = context->card; - // wait for the card to become ready - uint8_t ior = 0; - while ((ior & FUNC1_EN_MASK) == 0) { - err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); - if (err != ESP_OK) return err; - ESP_LOGI(TAG, "IOR: 0x%02x", ior); - } - return ESP_OK; -} - -static inline esp_err_t esp_slave_write_byte(esp_slave_context_t *context, uint32_t addr, uint8_t val, uint8_t *val_o) -{ - return sdmmc_io_write_byte(context->card, 1, addr&0x3FF, val, val_o); -} - -static inline esp_err_t esp_slave_write_bytes(esp_slave_context_t *context, uint32_t addr, uint8_t *val, int len) -{ - return sdmmc_io_write_bytes(context->card, 1, addr&0x3FF, val, len); -} - -static inline esp_err_t esp_slave_read_byte(esp_slave_context_t *context, uint32_t addr, uint8_t *val_o) -{ - return sdmmc_io_read_byte(context->card, 1, addr&0x3FF, val_o); -} - -static inline esp_err_t esp_slave_read_bytes(esp_slave_context_t *context, uint32_t addr, uint8_t *val_o, int len) -{ - return sdmmc_io_read_bytes(context->card, 1, addr&0x3FF, val_o, len); -} - -esp_err_t esp_slave_send_packet(esp_slave_context_t *context, const void* start, size_t length, uint32_t wait_ms) -{ - sdmmc_card_t *card = context->card; - uint16_t buffer_size = context->buffer_size; - int buffer_used = (length + buffer_size - 1)/buffer_size; - esp_err_t err; - const uint32_t wait_ticks = wait_ms/portTICK_PERIOD_MS; - uint32_t pre = xTaskGetTickCount(); - - assert(length>0); - for(;;) { - uint32_t num = 0; - err = esp_slave_get_tx_buffer_num(context, &num); - if (err == ESP_OK && num * buffer_size >= length) break; - if (err != ESP_OK && err != ESP_ERR_TIMEOUT) return err; - //not error and buffer not enough, retry ``timeout_cnt`` times - uint32_t now = xTaskGetTickCount(); - if (now-pre >= wait_ticks) { - ESP_LOGD(TAG, "buffer is not enough: %d, %d required.", num, buffer_used); - return ESP_ERR_TIMEOUT; - } else { - ESP_LOGV(TAG, "buffer is not enough: %d, %d required. Retry...", num, buffer_used); - } - vTaskDelay(1); - } - - ESP_LOGV(TAG, "send_packet: len: %d", length); - uint8_t *start_ptr = (uint8_t*)start; - uint32_t len_remain = length; - do { - const int block_size = 512; - /* Though the driver supports to split packet of unaligned size into - * length of 4x and 1~3, we still send aligned size of data to get - * higher effeciency. The length is determined by the SDIO address, and - * the remainning will be discard by the slave hardware. - */ - int block_n = len_remain/block_size; - int len_to_send; - if (block_n) { - len_to_send = block_n * block_size; - err = sdmmc_io_write_blocks(card, 1, ESP_SLAVE_CMD53_END_ADDR - len_remain, start_ptr, len_to_send); - } else { - len_to_send = len_remain; - err = sdmmc_io_write_bytes(card, 1, ESP_SLAVE_CMD53_END_ADDR - len_remain, start_ptr, (len_to_send + 3) & (~3)); - } - if (err != ESP_OK) return err; - start_ptr += len_to_send; - len_remain -= len_to_send; - } while (len_remain); - - context->tx_sent_buffers += buffer_used; - return ESP_OK; -} - -esp_err_t esp_slave_get_packet(esp_slave_context_t *context, void* out_data, size_t size, size_t *out_length, uint32_t wait_ms) -{ - sdmmc_card_t *card = context->card; - esp_err_t err; - esp_err_t ret = ESP_OK; - uint32_t len; - const uint32_t wait_ticks = wait_ms/portTICK_PERIOD_MS; - uint32_t pre = xTaskGetTickCount(); - - assert (size>0); - for (;;) { - err = esp_slave_get_rx_data_size(context, &len); - if (err == ESP_OK && len > 0) break; - if (err != ESP_OK && err != ESP_ERR_TIMEOUT) return err; - //not error and no data, retry ``timeout_cnt`` times. - uint32_t now = xTaskGetTickCount(); - if (now-pre >= wait_ticks) return ESP_ERR_NOT_FOUND; - vTaskDelay(1); - } - - ESP_LOGV(TAG, "get_packet: slave len=%d, max read size=%d", len, size); - if (len > size) { - len = size; - ret = ESP_ERR_NOT_FINISHED; - } - - uint8_t *start = out_data; - uint32_t len_remain = len; - do { - const int block_size = 512; //currently our driver don't support block size other than 512 - int len_to_send; - - int block_n = len_remain/block_size; - if (block_n != 0) { - len_to_send = block_n * block_size; - err = sdmmc_io_read_blocks(card, 1, ESP_SLAVE_CMD53_END_ADDR - len_remain, start, len_to_send); - } else { - len_to_send = len_remain; - /* though the driver supports to split packet of unaligned size into length - * of 4x and 1~3, we still get aligned size of data to get higher - * effeciency. The length is determined by the SDIO address, and the - * remainning will be ignored by the slave hardware. - */ - err = sdmmc_io_read_bytes(card, 1, ESP_SLAVE_CMD53_END_ADDR - len_remain, start, (len_to_send + 3) & (~3)); - } - if (err != ESP_OK) return err; - start += len_to_send; - len_remain -= len_to_send; - } while(len_remain!=0); - - context->rx_got_bytes += len; - *out_length = len; - return ret; -} - -esp_err_t esp_slave_get_tx_buffer_num(esp_slave_context_t *context, uint32_t* tx_num) -{ - uint32_t len; - esp_err_t err; - - ESP_LOGV(TAG, "get_tx_buffer_num"); - err = esp_slave_read_bytes(context, HOST_SLC0HOST_TOKEN_RDATA_REG, (uint8_t*)&len, 4); - if (err != ESP_OK) return err; - len = (len>>16)&TX_BUFFER_MASK; - len = (len + TX_BUFFER_MAX - context->tx_sent_buffers)%TX_BUFFER_MAX; - *tx_num = len; - return ESP_OK; -} - -esp_err_t esp_slave_get_rx_data_size(esp_slave_context_t *context, uint32_t* rx_size) -{ - uint32_t len; - esp_err_t err; - - ESP_LOGV(TAG, "get_rx_data_size: got_bytes: %d", context->rx_got_bytes); - err = esp_slave_read_bytes(context, HOST_SLCHOST_PKT_LEN_REG, (uint8_t*)&len, 4); - if (err != ESP_OK) return err; - len &= RX_BYTE_MASK; - len = (len + RX_BYTE_MAX - context->rx_got_bytes)%RX_BYTE_MAX; - *rx_size = len; - return ESP_OK; -} - -esp_err_t esp_slave_clear_intr(esp_slave_context_t *context, uint32_t intr_mask) -{ - ESP_LOGV(TAG, "clear_intr: %08X", intr_mask); - return esp_slave_write_bytes(context, HOST_SLC0HOST_INT_CLR_REG, (uint8_t*)&intr_mask, 4); -} - -esp_err_t esp_slave_get_intr(esp_slave_context_t *context, uint32_t *intr_raw, uint32_t *intr_st) -{ - esp_err_t r; - ESP_LOGV(TAG, "get_intr"); - if (intr_raw == NULL && intr_st == NULL) return ESP_ERR_INVALID_ARG; - - if (intr_raw != NULL) { - r= esp_slave_read_bytes(context, HOST_SLC0HOST_INT_RAW_REG, (uint8_t*)intr_raw, 4); - if (r != ESP_OK) return r; - } - if (intr_st != NULL) { - r = esp_slave_read_bytes(context, HOST_SLC0HOST_INT_ST_REG, (uint8_t*)intr_st, 4); - if (r != ESP_OK) return r; - } - return ESP_OK; -} - -esp_err_t esp_slave_set_intr_ena(esp_slave_context_t *context, uint32_t ena_mask) -{ - ESP_LOGV(TAG, "set_intr_ena: %08X", ena_mask); - return esp_slave_write_bytes(context, HOST_SLC0HOST_INT_ENA_REG, (uint8_t*)&ena_mask, 4); -} - -esp_err_t esp_slave_get_intr_ena(esp_slave_context_t *context, uint32_t *ena_mask_o) -{ - ESP_LOGV(TAG, "get_intr_ena"); - esp_err_t ret = esp_slave_read_bytes(context, HOST_SLC0HOST_INT_ENA_REG, (uint8_t*)ena_mask_o, 4); - ESP_LOGV(TAG, "ena: %08X", *ena_mask_o); - return ret; -} - -esp_err_t esp_slave_write_reg(esp_slave_context_t *context, uint8_t addr, uint8_t value, uint8_t* value_o) -{ - ESP_LOGV(TAG, "write_reg: %08X", value); - // addrress over range - if (addr >= 64) return ESP_ERR_INVALID_ARG; - // reserved for interrupts - if (addr >= 28 && addr <= 31) return ESP_ERR_INVALID_ARG; - return esp_slave_write_byte(context, HOST_SLCHOST_CONF_W_REG(addr), value, value_o); -} - -esp_err_t esp_slave_read_reg(esp_slave_context_t *context, uint8_t add, uint8_t *value_o) -{ - ESP_LOGV(TAG, "read_reg"); - // address over range - if (add >= 64) return ESP_ERR_INVALID_ARG; - esp_err_t ret = esp_slave_read_byte(context, HOST_SLCHOST_CONF_W_REG(add), value_o); - ESP_LOGV(TAG, "reg: %08X", *value_o); - return ret; -} - -esp_err_t esp_slave_send_slave_intr(esp_slave_context_t *context, uint8_t intr_mask) -{ - ESP_LOGV(TAG, "send_slave_intr: %02x", intr_mask); - return esp_slave_write_byte(context, HOST_SLCHOST_CONF_W7_REG+0, intr_mask, NULL); -} - - diff --git a/examples/peripherals/sdio/host/components/esp_slave/include/esp_slave.h b/examples/peripherals/sdio/host/components/esp_slave/include/esp_slave.h deleted file mode 100644 index c2d4d31d24..0000000000 --- a/examples/peripherals/sdio/host/components/esp_slave/include/esp_slave.h +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "sdmmc_cmd.h" -#include "driver/sdmmc_defs.h" -#include "soc/sdio_slave_periph.h" - -/* - * NOTE: This component is for example purpose only. Assertion fails if any of - * the preconditions (connections, grounding, slave data preparation, etc.) is - * not met. - * Please do check and handle the return value in your real product. -*/ - -#define ESP_ERR_NOT_FINISHED 0x201 - -/** Context used by the ``esp_slave`` component. - */ -typedef struct { - sdmmc_card_t* card; ///< Initialized sdmmc_cmd card - uint16_t buffer_size; - ///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. - ///< Buffer size of the slave pre-defined between host and slave before communication. - uint16_t block_size; - ///< If this is too large, it takes time to send stuff bits; while if too small, intervals between blocks cost much. - ///< Should be set according to length of data, and larger than ``TRANS_LEN_MAX/511``. - ///< Block size of the SDIO function 1. After the initialization this will hold the value the slave really do. Valid value is 1-2048. - size_t tx_sent_buffers; ///< Counter hold the amount of buffers already sent to ESP32 slave. Should be set to 0 when initialization. - size_t rx_got_bytes; ///< Counter hold the amount of bytes already received from ESP32 slave. Should be set to 0 when initialization. -} esp_slave_context_t; - -/** Initialize ``esp_slave_context_t`` by this macro. -*/ -#define ESP_SLAVE_DEFAULT_CONTEXT(card) (esp_slave_context_t){\ - .card = card, \ - .block_size = 0x200, \ - .buffer_size = 128, \ - .tx_sent_buffers = 0, \ - .rx_got_bytes = 0, \ - } - -/** SDIO Initialize process of a ESP32 slave device. - * - * @param context Context of the ``esp_slave`` component. Send to other functions later. - * - * @return - * - ESP_OK if success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_init_io(esp_slave_context_t *context); - -/** Wait for interrupt of a ESP32 slave device. - * - * @param context Context of the ``esp_slave`` component. - * - * @return - * - ESP_OK if success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_wait_for_ioready(esp_slave_context_t *context); - -/** Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``. - * - * @param context Context of the component. - * @param tx_num Output of buffer num that host can send data to ESP32 slave. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_get_tx_buffer_num(esp_slave_context_t *context, uint32_t* tx_num); - -/** Get amount of data the ESP32 slave preparing to send to host. - * - * @param context Context of the component. - * @param rx_size Output of data size to read from slave. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_get_rx_data_size(esp_slave_context_t *context, uint32_t* rx_size); - - -/** Reset the counters of this component. Usually you don't need to do this unless you know the slave is reset. - * - * @param context Context of the component. - */ -inline static void esp_slave_reset_cnt(esp_slave_context_t *context) -{ - context->rx_got_bytes = 0; - context->tx_sent_buffers = 0; -} - -/** Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` in the context. - * - * @param context Context of the component. - * @param start Start address of the packet to send - * @param length Length of data to send, if the packet is over-size, the it will be divided into blocks and hold into different buffers automatically. - * @param wait_ms Time to wait before timeout, in ms. - * - * @return - * - ESP_OK Success - * - ESP_ERR_TIMEOUT No buffer to use, or error ftrom SDMMC host controller - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_send_packet(esp_slave_context_t *context, const void* start, size_t length, uint32_t wait_ms); - -/** Get a packet from ESP32 slave. - * - * @param context Context of the component. - * @param[out] out_data Data output address - * @param size The size of the output buffer, if the buffer is smaller than the size of data to receive from slave, the driver returns ``ESP_ERR_NOT_FINISHED`` - * @param[out] out_length Output of length the data actually received from slave. - * @param wait_ms Time to wait before timeout, in ms. - * - * @return - * - ESP_OK Success, all the data are read from the slave. - * - ESP_ERR_NOT_FINISHED Read success, while there're data remaining. - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_get_packet(esp_slave_context_t *context, void* out_data, size_t size, size_t *out_length, uint32_t wait_ms); - -/** wait for an interrupt of the slave - * - * @param context Context of the component. - * @param wait Ticks to wait. - * - * @return - * - ESP_ERR_NOT_SUPPORTED Currently our driver doesnot support SDIO with SPI interface. - * - ESP_OK If interrupt triggered. - * - ESP_ERR_TIMEOUT No interrupts before timeout. - */ -inline static esp_err_t esp_slave_wait_int(esp_slave_context_t *context, TickType_t wait) -{ - return sdmmc_io_wait_int(context->card, wait); -} - -/** Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same. - * - * @param context Context of the component. - * @param intr_mask Mask of interrupt bits to clear. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_clear_intr(esp_slave_context_t *context, uint32_t intr_mask); - -/** Get interrupt bits of ESP32 slave. - * - * @param context Context of the component. - * @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read. - * @param intr_st Output of the masked interrupt bits. set to NULL if only raw bits are read. - * - * @return - * - ESP_OK Success - * - ESP_INVALID_ARG if both ``intr_raw`` and ``intr_st`` are NULL. - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_get_intr(esp_slave_context_t *context, uint32_t *intr_raw, uint32_t *intr_st); - -/** Set interrupt enable bits of ESP32 slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set. - * - * @param context Context of the component. - * @param ena_mask Mask of the interrupt bits to enable. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_set_intr_ena(esp_slave_context_t *context, uint32_t ena_mask); - -/** Get interrupt enable bits of ESP32 slave. - * - * @param context Context of the component. - * @param ena_mask_o Output of interrupt bit enable mask. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_get_intr_ena(esp_slave_context_t *context, uint32_t *ena_mask_o); - -/** Write general purpose R/W registers (8-bit) of ESP32 slave. - * - * @param context Context of the component. - * @param addr Address of register to write. Valid address: 0-27, 32-63 (28-31 reserved). - * @param value Value to write to the register. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Address not valid. - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_write_reg(esp_slave_context_t *context, uint8_t addr, uint8_t value, uint8_t* value_o); - -/** Read general purpose R/W registers (8-bit) of ESP32 slave. - * - * @param context Context of the component. - * @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). - * @param value Output value read from the register. - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Address not valid. - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_read_reg(esp_slave_context_t *context, uint8_t add, uint8_t *value_o); - -/** Send interrupts to slave. Each bit of the interrupt will be triggered. - * - * @param context Context of the component. - * @param intr_mask Mask of interrupt bits to send to slave. - * - * @return - * - ESP_OK Success - * - One of the error codes from SDMMC host controller - */ -esp_err_t esp_slave_send_slave_intr(esp_slave_context_t *context, uint8_t intr_mask); - - diff --git a/examples/peripherals/sdio/host/main/Kconfig.projbuild b/examples/peripherals/sdio/host/main/Kconfig.projbuild index 7fb21ebd19..ad79d6587a 100644 --- a/examples/peripherals/sdio/host/main/Kconfig.projbuild +++ b/examples/peripherals/sdio/host/main/Kconfig.projbuild @@ -33,25 +33,21 @@ menu "Example Configuration" If the example does not work, please try disabling the HS mode. choice EXAMPLE_SLAVE - prompt "Id of Slave used in Espressif master-slave board." + prompt "GPIO to control slave EN in Espressif master-slave board." default EXAMPLE_SLAVE_NONE help - If Espressif master-slave board is used, select which slave is used. + If Espressif master-slave board is used, select the correct GPIO to control slave's EN. config EXAMPLE_SLAVE_NONE bool "Not using Espressif master-slave board." config EXAMPLE_SLAVE_B1 bool "Using slave B1" - config EXAMPLE_SLAVE_B2 - bool "Using slave B2" - config EXAMPLE_SLAVE_B3 - bool "Using slave B3" endchoice config EXAMPLE_SLAVE_PWR_NEGTIVE_ACTIVE bool "Slave power control pin is negtive active, otherwise postive active" depends on !EXAMPLE_SLAVE_NONE - default y + default n help Slave power control pin is negtive active, otherwise postive active diff --git a/examples/peripherals/sdio/host/main/app_main.c b/examples/peripherals/sdio/host/main/app_main.c index 840126e963..c5eb083137 100644 --- a/examples/peripherals/sdio/host/main/app_main.c +++ b/examples/peripherals/sdio/host/main/app_main.c @@ -1,4 +1,4 @@ -/* SDIO example, host (uses sdmmc host driver) +/* SDIO example, host (uses sdmmc_host/sdspi_host driver) This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -20,26 +20,18 @@ #include "soc/sdio_slave_periph.h" #include "esp_log.h" #include "esp_attr.h" -#include "esp_slave.h" +#include "esp_serial_slave_link/essl_sdio.h" #include "sdkconfig.h" #include "driver/sdmmc_host.h" #include "driver/sdspi_host.h" +#define TIMEOUT_MAX UINT32_MAX -/* - * For SDIO master-slave board, we have 3 pins controlling power of 3 different - * slaves individially. We only enable one at a time. - */ -#define GPIO_B1 5 -#define GPIO_B2 18 -#define GPIO_B3 19 + +#define GPIO_B1 21 #if CONFIG_EXAMPLE_SLAVE_B1 #define SLAVE_PWR_GPIO GPIO_B1 -#elif CONFIG_EXAMPLE_SLAVE_B2 -#define SLAVE_PWR_GPIO GPIO_B2 -#elif CONFIG_EXAMPLE_SLAVE_B3 -#define SLAVE_PWR_GPIO GPIO_B3 #endif /* @@ -85,6 +77,7 @@ #define WRITE_BUFFER_LEN 4096 #define READ_BUFFER_LEN 4096 +#define SLAVE_BUFFER_SIZE 128 static const char TAG[] = "example_host"; @@ -103,21 +96,21 @@ typedef enum { } example_job_t; //host use this to inform the slave it should reset its counters -esp_err_t slave_reset(esp_slave_context_t *context) +esp_err_t slave_reset(essl_handle_t handle) { esp_err_t ret; ESP_LOGI(TAG, "send reset to slave..."); - ret = esp_slave_write_reg(context, 0, JOB_RESET, NULL); + ret = essl_write_reg(handle, 0, JOB_RESET, NULL, TIMEOUT_MAX); if (ret != ESP_OK) { return ret; } - ret = esp_slave_send_slave_intr(context, BIT(SLAVE_INTR_NOTIFY)); + ret = essl_send_slave_intr(handle, BIT(SLAVE_INTR_NOTIFY), TIMEOUT_MAX); if (ret != ESP_OK) { return ret; } vTaskDelay(500 / portTICK_RATE_MS); - ret = esp_slave_wait_for_ioready(context); + ret = essl_wait_for_ready(handle, TIMEOUT_MAX); ESP_LOGI(TAG, "slave io ready"); return ret; } @@ -166,7 +159,7 @@ static esp_err_t print_sdio_cis_information(sdmmc_card_t* card) } //host use this to initialize the slave card as well as SDIO registers -esp_err_t slave_init(esp_slave_context_t *context) +esp_err_t slave_init(essl_handle_t* handle) { esp_err_t err; /* Probe */ @@ -247,8 +240,14 @@ esp_err_t slave_init(esp_slave_context_t *context) gpio_pullup_en(13); gpio_pulldown_dis(13); - *context = ESP_SLAVE_DEFAULT_CONTEXT(card); - esp_err_t ret = esp_slave_init_io(context); + essl_sdio_config_t ser_config = { + .card = card, + .recv_buffer_size = SLAVE_BUFFER_SIZE, + }; + err = essl_sdio_init_dev(handle, &ser_config); + ESP_ERROR_CHECK(err); + + esp_err_t ret = essl_init(*handle, TIMEOUT_MAX); ESP_ERROR_CHECK(ret); ret = print_sdio_cis_information(card); @@ -267,7 +266,7 @@ void slave_power_on(void) level_active = 1; #endif gpio_config_t cfg = { - .pin_bit_mask = BIT64(GPIO_B1) | BIT64(GPIO_B2) | BIT64(GPIO_B3), + .pin_bit_mask = BIT64(GPIO_B1), .mode = GPIO_MODE_DEF_OUTPUT, .pull_up_en = false, .pull_down_en = false, @@ -275,8 +274,6 @@ void slave_power_on(void) }; gpio_config(&cfg); gpio_set_level(GPIO_B1, !level_active); - gpio_set_level(GPIO_B2, !level_active); - gpio_set_level(GPIO_B3, !level_active); vTaskDelay(100); gpio_set_level(SLAVE_PWR_GPIO, level_active); @@ -288,18 +285,18 @@ void slave_power_on(void) DMA_ATTR uint8_t rcv_buffer[READ_BUFFER_LEN]; //try to get an interrupt from the slave and handle it, return if none. -esp_err_t process_event(esp_slave_context_t *context) +esp_err_t process_event(essl_handle_t handle) { - esp_err_t ret = esp_slave_wait_int(context, 0); + esp_err_t ret = essl_wait_int(handle, 0); if (ret == ESP_ERR_TIMEOUT) { return ret; } ESP_ERROR_CHECK(ret); uint32_t intr_raw, intr_st; - ret = esp_slave_get_intr(context, &intr_raw, &intr_st); + ret = essl_get_intr(handle, &intr_raw, &intr_st, TIMEOUT_MAX); ESP_ERROR_CHECK(ret); - ret = esp_slave_clear_intr(context, intr_raw); + ret = essl_clear_intr(handle, intr_raw, TIMEOUT_MAX); ESP_ERROR_CHECK(ret); ESP_LOGD(TAG, "intr: %08X", intr_raw); @@ -314,7 +311,7 @@ esp_err_t process_event(esp_slave_context_t *context) ESP_LOGD(TAG, "new packet coming"); while (1) { size_t size_read = READ_BUFFER_LEN; - ret = esp_slave_get_packet(context, rcv_buffer, READ_BUFFER_LEN, &size_read, wait_ms); + ret = essl_get_packet(handle, rcv_buffer, READ_BUFFER_LEN, &size_read, wait_ms); if (ret == ESP_ERR_NOT_FOUND) { ESP_LOGE(TAG, "interrupt but no data can be read"); break; @@ -334,32 +331,32 @@ esp_err_t process_event(esp_slave_context_t *context) } //tell the slave to do a job -static inline esp_err_t slave_inform_job(esp_slave_context_t *context, example_job_t job) +static inline esp_err_t slave_inform_job(essl_handle_t handle, example_job_t job) { esp_err_t ret; - ret = esp_slave_write_reg(context, SLAVE_REG_JOB, job, NULL); + ret = essl_write_reg(handle, SLAVE_REG_JOB, job, NULL, TIMEOUT_MAX); ESP_ERROR_CHECK(ret); - ret = esp_slave_send_slave_intr(context, BIT(SLAVE_INTR_NOTIFY)); + ret = essl_send_slave_intr(handle, BIT(SLAVE_INTR_NOTIFY), TIMEOUT_MAX); ESP_ERROR_CHECK(ret); return ret; } //tell the slave to write registers by write one of them, and read them back -void job_write_reg(esp_slave_context_t *context, int value) +void job_write_reg(essl_handle_t handle, int value) { esp_err_t ret; - uint8_t reg_read[64]; + uint8_t reg_read[60]; ESP_LOGI(TAG, "========JOB: write slave reg========"); - ret = esp_slave_write_reg(context, SLAVE_REG_VALUE, value, NULL); + ret = essl_write_reg(handle, SLAVE_REG_VALUE, value, NULL, TIMEOUT_MAX); ESP_ERROR_CHECK(ret); - ret = slave_inform_job(context, JOB_WRITE_REG); + ret = slave_inform_job(handle, JOB_WRITE_REG); ESP_ERROR_CHECK(ret); vTaskDelay(10); - for (int i = 0; i < 64; i++) { + for (int i = 0; i < 60; i++) { ESP_LOGD(TAG, "reading register %d", i); - ret = esp_slave_read_reg(context, i, ®_read[i]); + ret = essl_read_reg(handle, i, ®_read[i], TIMEOUT_MAX); ESP_ERROR_CHECK(ret); } @@ -374,7 +371,7 @@ int packet_len[] = {6, 12, 1024, 512, 3, 513, 517}; DMA_ATTR uint8_t send_buffer[READ_BUFFER_LEN]; //send packets to the slave (they will return and be handled by the interrupt handler) -void job_fifo(esp_slave_context_t *context) +void job_fifo(essl_handle_t handle) { for (int i = 0; i < READ_BUFFER_LEN; i++) { send_buffer[i] = 0x46 + i * 5; @@ -392,7 +389,7 @@ void job_fifo(esp_slave_context_t *context) for (int i = 0; i < sizeof(packet_len) / sizeof(int); i++) { const int wait_ms = 50; int length = packet_len[i]; - ret = esp_slave_send_packet(context, send_buffer + pointer, length, wait_ms); + ret = essl_send_packet(handle, send_buffer + pointer, length, wait_ms); if (ret == ESP_ERR_TIMEOUT) { ESP_LOGD(TAG, "several packets are expected to timeout."); } else { @@ -404,15 +401,15 @@ void job_fifo(esp_slave_context_t *context) } //inform the slave to send interrupts to host (the interrupts will be handled in the interrupt handler) -void job_getint(esp_slave_context_t *context) +void job_getint(essl_handle_t handle) { ESP_LOGI(TAG, "========JOB: get interrupts from slave========"); - slave_inform_job(context, JOB_SEND_INT); + slave_inform_job(handle, JOB_SEND_INT); } void app_main(void) { - esp_slave_context_t context; + essl_handle_t handle; esp_err_t err; //enable the power if on espressif SDIO master-slave board @@ -420,23 +417,23 @@ void app_main(void) ESP_LOGI(TAG, "host ready, start initializing slave..."); - err = slave_init(&context); + err = slave_init(&handle); ESP_ERROR_CHECK(err); - err = slave_reset(&context); + err = slave_reset(handle); ESP_ERROR_CHECK(err); uint32_t start, end; - job_write_reg(&context, 10); + job_write_reg(handle, 10); int times = 2; while (1) { - job_getint(&context); + job_getint(handle); start = xTaskGetTickCount(); while (1) { - process_event(&context); + process_event(handle); vTaskDelay(1); end = xTaskGetTickCount(); if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 5000) { @@ -449,11 +446,11 @@ void app_main(void) }; while (1) { - job_fifo(&context); + job_fifo(handle); start = xTaskGetTickCount(); while (1) { - process_event(&context); + process_event(handle); vTaskDelay(1); end = xTaskGetTickCount(); if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 2000) { diff --git a/tools/ci/config/target-test.yml b/tools/ci/config/target-test.yml index c1a3673a56..731540e51a 100644 --- a/tools/ci/config/target-test.yml +++ b/tools/ci/config/target-test.yml @@ -492,6 +492,21 @@ UT_043: - UT_T1_32kXTAL - psram +UT_044: + extends: .unit_test_template + parallel: 4 + tags: + - ESP32_IDF + - UT_SDIO + +UT_045: + extends: .unit_test_template + tags: + - ESP32_IDF + - UT_SDIO + - psram + + nvs_compatible_test: extends: .test_template artifacts: