From fe588a4815e1e35e4b3168a3b0f9950c46b09766 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Wed, 9 Feb 2022 16:23:26 +0800 Subject: [PATCH] Add essl to component manager --- .github/workflows/upload_component.yml | 2 +- esp_serial_slave_link/CMakeLists.txt | 9 + esp_serial_slave_link/LICENSE | 202 +++++++ esp_serial_slave_link/README.md | 9 + esp_serial_slave_link/essl.c | 234 ++++++++ esp_serial_slave_link/essl_internal.h | 38 ++ esp_serial_slave_link/essl_sdio.c | 499 ++++++++++++++++++ esp_serial_slave_link/essl_spi.c | 491 +++++++++++++++++ esp_serial_slave_link/idf_component.yml | 5 + .../include/esp_serial_slave_link/essl.h | 226 ++++++++ .../include/esp_serial_slave_link/essl_sdio.h | 247 +++++++++ .../include/esp_serial_slave_link/essl_spi.h | 312 +++++++++++ .../include/essl_spi/esp32c2_defs.h | 30 ++ .../include/essl_spi/esp32c3_defs.h | 29 + .../include/essl_spi/esp32s2_defs.h | 29 + .../include/essl_spi/esp32s3_defs.h | 29 + test_app/CMakeLists.txt | 2 +- 17 files changed, 2391 insertions(+), 2 deletions(-) create mode 100644 esp_serial_slave_link/CMakeLists.txt create mode 100644 esp_serial_slave_link/LICENSE create mode 100644 esp_serial_slave_link/README.md create mode 100644 esp_serial_slave_link/essl.c create mode 100644 esp_serial_slave_link/essl_internal.h create mode 100644 esp_serial_slave_link/essl_sdio.c create mode 100644 esp_serial_slave_link/essl_spi.c create mode 100644 esp_serial_slave_link/idf_component.yml create mode 100644 esp_serial_slave_link/include/esp_serial_slave_link/essl.h create mode 100644 esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h create mode 100644 esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h create mode 100644 esp_serial_slave_link/include/essl_spi/esp32c2_defs.h create mode 100644 esp_serial_slave_link/include/essl_spi/esp32c3_defs.h create mode 100644 esp_serial_slave_link/include/essl_spi/esp32s2_defs.h create mode 100644 esp_serial_slave_link/include/essl_spi/esp32s3_defs.h diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 425de32..7c6140e 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -15,6 +15,6 @@ jobs: - name: Upload components to component service uses: espressif/upload-components-ci-action@v1 with: - directories: "bdc_motor;cbor;jsmn;led_strip;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap;json_generator;json_parser;usb/usb_host_cdc_acm;usb/usb_host_msc;usb/usb_host_uvc" + directories: "bdc_motor;cbor;jsmn;led_strip;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap;json_generator;json_parser;usb/usb_host_cdc_acm;usb/usb_host_msc;usb/usb_host_uvc;esp_serial_slave_link" namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/esp_serial_slave_link/CMakeLists.txt b/esp_serial_slave_link/CMakeLists.txt new file mode 100644 index 0000000..e9cb0fe --- /dev/null +++ b/esp_serial_slave_link/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRCS "essl.c" + "essl_sdio.c" + "essl_spi.c" + INCLUDE_DIRS "include" + REQUIRES "sdmmc" + "driver" + PRIV_INCLUDE_DIRS "." + "include/esp_serial_slave_link" +) diff --git a/esp_serial_slave_link/LICENSE b/esp_serial_slave_link/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/esp_serial_slave_link/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/esp_serial_slave_link/README.md b/esp_serial_slave_link/README.md new file mode 100644 index 0000000..ac44ba9 --- /dev/null +++ b/esp_serial_slave_link/README.md @@ -0,0 +1,9 @@ +# Espressif Serial Slave Link (ESSL) component + +This component used to reside in [esp-idf](https://github.com/espressif/esp-idf) project as its component. + +It's used on the HOST, to communicate with ESP chips as SLAVE via SDIO/SPI slave HD mode. + +The port layer (`essl_sdio.c/essl_spi.c`) are currently only written to run on ESP chips in master mode, but you may also modify them to work on more platforms. + +See more documentation: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_serial_slave_link.html \ No newline at end of file diff --git a/esp_serial_slave_link/essl.c b/esp_serial_slave_link/essl.c new file mode 100644 index 0000000..b97c507 --- /dev/null +++ b/esp_serial_slave_link/essl.c @@ -0,0 +1,234 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "essl.h" +#include "essl_internal.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 || 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 is not ready, retry + } while (remain_wait_ms > 0); + return err; +} + +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/esp_serial_slave_link/essl_internal.h b/esp_serial_slave_link/essl_internal.h new file mode 100644 index 0000000..5f6c56d --- /dev/null +++ b/esp_serial_slave_link/essl_internal.h @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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/esp_serial_slave_link/essl_sdio.c b/esp_serial_slave_link/essl_sdio.c new file mode 100644 index 0000000..70718b0 --- /dev/null +++ b/esp_serial_slave_link/essl_sdio.c @@ -0,0 +1,499 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#include "esp_log.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_defs.h" + +#include "essl_internal.h" +#include "essl_sdio.h" + +#if SOC_SDIO_SLAVE_SUPPORTED +#include "soc/host_reg.h" + +static const char TAG[] = "essl_sdio"; + +#define HOST_SLCHOST_CONF_W_REG(pos) (HOST_SLCHOST_CONF_W0_REG+pos+(pos>23?4:0)+(pos>31?12:0)) + +#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 = 0; + 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%02"PRIx8, 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%02"PRIx8, 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%02"PRIx8, 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%02"PRIx8, ior); + } + + // get interrupt status + uint8_t ie = 0; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie); + if (err != ESP_OK) { + return err; + } + ESP_LOGD(TAG, "IE: 0x%02"PRIx8, 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%02"PRIx8, ie); + + // get bus width register + uint8_t bus_width = 0; + 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%02"PRIx8, 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%02"PRIx8, 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: %d", (unsigned 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: %d", (unsigned 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: %d", (unsigned 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: %d", (unsigned 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", (unsigned int)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: 0x%02"PRIX8, 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: %02"PRIX8, *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: %08"PRIX32, 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: %08"PRIX32, 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: %08"PRIX32, *ena_mask_o); + return ret; +} + +esp_err_t essl_sdio_send_slave_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms) +{ + //Only 8 bits available + ESP_LOGV(TAG, "send_slave_intr: %02"PRIx8, (uint8_t)intr_mask); + return essl_sdio_write_byte(((essl_sdio_context_t *)arg)->card, HOST_SLCHOST_CONF_W7_REG + 0, (uint8_t)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; +} + +#endif // #if SOC_SDIO_SLAVE_SUPPORTED diff --git a/esp_serial_slave_link/essl_spi.c b/esp_serial_slave_link/essl_spi.c new file mode 100644 index 0000000..af2235b --- /dev/null +++ b/esp_serial_slave_link/essl_spi.c @@ -0,0 +1,491 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "esp_memory_utils.h" +#include "esp_private/periph_ctrl.h" + +#include "driver/spi_master.h" +#include "hal/spi_types.h" +#include "hal/spi_ll.h" + +#include "essl_internal.h" +#include "essl_spi.h" + +/** + * Initialise device function list of SPI by this macro. + */ +#define ESSL_SPI_DEFAULT_DEV_FUNC() (essl_dev_t) {\ + .get_tx_buffer_num = essl_spi_get_tx_buffer_num,\ + .update_tx_buffer_num = essl_spi_update_tx_buffer_num,\ + .get_rx_data_size = essl_spi_get_rx_data_size,\ + .update_rx_data_size = essl_spi_update_rx_data_size,\ + .send_packet = essl_spi_send_packet,\ + .get_packet = essl_spi_get_packet,\ + .write_reg = essl_spi_write_reg,\ + .read_reg = essl_spi_read_reg,\ +} + +static const char TAG[] = "essl_spi"; + +typedef struct { + spi_device_handle_t spi; // Pointer to SPI device handle. + /* Master TX, Slave RX */ + struct { + size_t sent_buf_num; // Number of TX buffers that has been sent out by the master. + size_t slave_rx_buf_num; // Number of RX buffers laoded by the slave. + uint16_t tx_buffer_size; /* Buffer size for Master TX / Slave RX direction. + * Data with length within this size will still be regarded as one buffer. + * E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. */ + uint8_t tx_sync_reg; // The pre-negotiated register ID for Master-TX-SLAVE-RX synchronization. 1 word (4 Bytes) will be reserved for the synchronization. + } master_out; + /* Master RX, Slave TX */ + struct { + size_t received_bytes; // Number of the RX bytes that has been received by the Master. + size_t slave_tx_bytes; // Number of the TX bytes that has been loaded by the Slave + uint8_t rx_sync_reg; // The pre-negotiated register ID for Master-RX-SLAVE-TX synchronization. 1 word (4 Bytes) will be reserved for the synchronization. + } master_in; +} essl_spi_context_t; + +static uint16_t get_hd_command(spi_command_t cmd_t, uint32_t flags) +{ + spi_line_mode_t line_mode = { + .cmd_lines = 1, + }; + + if (flags & SPI_TRANS_MODE_DIO) { + line_mode.data_lines = 2; + if (flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + line_mode.addr_lines = 2; + } else { + line_mode.addr_lines = 1; + } + } else if (flags & SPI_TRANS_MODE_QIO) { + line_mode.data_lines = 4; + if (flags & SPI_TRANS_MODE_DIOQIO_ADDR) { + line_mode.addr_lines = 4; + } else { + line_mode.addr_lines = 1; + } + } else { + line_mode.data_lines = 1; + line_mode.addr_lines = 1; + } + + return spi_ll_get_slave_hd_command(cmd_t, line_mode); +} + +static int get_hd_dummy_bits(uint32_t flags) +{ + spi_line_mode_t line_mode = {}; + + if (flags & SPI_TRANS_MODE_DIO) { + line_mode.data_lines = 2; + } else if (flags & SPI_TRANS_MODE_QIO) { + line_mode.data_lines = 4; + } else { + line_mode.data_lines = 1; + } + + return spi_ll_get_slave_hd_dummy_bits(line_mode); +} + +esp_err_t essl_spi_rdbuf(spi_device_handle_t spi, uint8_t *out_data, int addr, int len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_RDBUF, flags), + .addr = addr % 72, + .rxlength = len * 8, + .rx_buffer = out_data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + + return spi_device_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_rdbuf_polling(spi_device_handle_t spi, uint8_t *out_data, int addr, int len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_RDBUF, flags), + .addr = addr % 72, + .rxlength = len * 8, + .rx_buffer = out_data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + + return spi_device_polling_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_wrbuf(spi_device_handle_t spi, const uint8_t *data, int addr, int len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_WRBUF, flags), + .addr = addr % 72, + .length = len * 8, + .tx_buffer = data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + return spi_device_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_wrbuf_polling(spi_device_handle_t spi, const uint8_t *data, int addr, int len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_WRBUF, flags), + .addr = addr % 72, + .length = len * 8, + .tx_buffer = data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + return spi_device_polling_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_rddma_seg(spi_device_handle_t spi, uint8_t *out_data, int seg_len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_RDDMA, flags), + .rxlength = seg_len * 8, + .rx_buffer = out_data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + return spi_device_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_rddma_done(spi_device_handle_t spi, uint32_t flags) +{ + spi_transaction_t end_t = { + .cmd = get_hd_command(SPI_CMD_HD_INT0, flags), + .flags = flags, + }; + return spi_device_transmit(spi, &end_t); +} + +esp_err_t essl_spi_rddma(spi_device_handle_t spi, uint8_t *out_data, int len, int seg_len, uint32_t flags) +{ + if (!esp_ptr_dma_capable(out_data) || ((intptr_t)out_data % 4) != 0) { + return ESP_ERR_INVALID_ARG; + } + seg_len = (seg_len > 0) ? seg_len : len; + + uint8_t *read_ptr = out_data; + esp_err_t ret = ESP_OK; + while (len > 0) { + int send_len = MIN(seg_len, len); + + ret = essl_spi_rddma_seg(spi, read_ptr, send_len, flags); + if (ret != ESP_OK) { + return ret; + } + + len -= send_len; + read_ptr += send_len; + } + return essl_spi_rddma_done(spi, flags); +} + +esp_err_t essl_spi_wrdma_seg(spi_device_handle_t spi, const uint8_t *data, int seg_len, uint32_t flags) +{ + spi_transaction_ext_t t = { + .base = { + .cmd = get_hd_command(SPI_CMD_HD_WRDMA, flags), + .length = seg_len * 8, + .tx_buffer = data, + .flags = flags | SPI_TRANS_VARIABLE_DUMMY, + }, + .dummy_bits = get_hd_dummy_bits(flags), + }; + return spi_device_transmit(spi, (spi_transaction_t *)&t); +} + +esp_err_t essl_spi_wrdma_done(spi_device_handle_t spi, uint32_t flags) +{ + spi_transaction_t end_t = { + .cmd = get_hd_command(SPI_CMD_HD_WR_END, flags), + .flags = flags, + }; + return spi_device_transmit(spi, &end_t); +} + +esp_err_t essl_spi_wrdma(spi_device_handle_t spi, const uint8_t *data, int len, int seg_len, uint32_t flags) +{ + if (!esp_ptr_dma_capable(data)) { + return ESP_ERR_INVALID_ARG; + } + seg_len = (seg_len > 0) ? seg_len : len; + + while (len > 0) { + int send_len = MIN(seg_len, len); + + esp_err_t ret = essl_spi_wrdma_seg(spi, data, send_len, flags); + if (ret != ESP_OK) { + return ret; + } + + len -= send_len; + data += send_len; + } + + return essl_spi_wrdma_done(spi, flags); +} + +esp_err_t essl_spi_int(spi_device_handle_t spi, int int_n, uint32_t flags) +{ + spi_transaction_t end_t = { + .cmd = get_hd_command(SPI_CMD_HD_INT0 + int_n, flags), + .flags = flags, + }; + return spi_device_transmit(spi, &end_t); +} + +//------------------------------------ APPEND MODE ----------------------------------// +static uint32_t essl_spi_get_rx_data_size(void *arg); +static esp_err_t essl_spi_update_rx_data_size(void *arg, uint32_t wait_ms); +static uint32_t essl_spi_get_tx_buffer_num(void *arg); +static esp_err_t essl_spi_update_tx_buffer_num(void *arg, uint32_t wait_ms); + +esp_err_t essl_spi_init_dev(essl_handle_t *out_handle, const essl_spi_config_t *init_config) +{ + ESP_RETURN_ON_FALSE(init_config->spi, ESP_ERR_INVALID_STATE, TAG, "Check SPI initialization first"); + ESP_RETURN_ON_FALSE(init_config->tx_sync_reg <= (SOC_SPI_MAXIMUM_BUFFER_SIZE - 1) * 4, ESP_ERR_INVALID_ARG, TAG, "GPSPI supports %d-byte-width internal registers", SOC_SPI_MAXIMUM_BUFFER_SIZE); + ESP_RETURN_ON_FALSE(init_config->rx_sync_reg <= (SOC_SPI_MAXIMUM_BUFFER_SIZE - 1) * 4, ESP_ERR_INVALID_ARG, TAG, "GPSPI supports %d-byte-width internal registers", SOC_SPI_MAXIMUM_BUFFER_SIZE); + ESP_RETURN_ON_FALSE(init_config->tx_sync_reg != init_config->rx_sync_reg, ESP_ERR_INVALID_ARG, TAG, "Should use different word of registers for synchronization"); + + essl_spi_context_t *context = calloc(1, sizeof(essl_spi_context_t)); + essl_dev_t *dev = calloc(1, sizeof(essl_dev_t)); + if (!context || !dev) { + free(context); + free(dev); + return ESP_ERR_NO_MEM; + } + + *context = (essl_spi_context_t) { + .spi = *init_config->spi, + .master_out.tx_buffer_size = init_config->tx_buf_size, + .master_out.tx_sync_reg = init_config->tx_sync_reg, + .master_in.rx_sync_reg = init_config->rx_sync_reg + }; + + *dev = ESSL_SPI_DEFAULT_DEV_FUNC(); + dev->args = context; + + *out_handle = dev; + + return ESP_OK; +} + +esp_err_t essl_spi_deinit_dev(essl_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, TAG, "ESSL SPI is not in use"); + free(handle->args); + free(handle); + return ESP_OK; +} + +void essl_spi_reset_cnt(void *arg) +{ + essl_spi_context_t *ctx = arg; + if (ctx) { + ctx->master_out.sent_buf_num = 0; + ctx->master_in.received_bytes = 0; + } +} + +//------------------------------------ RX ----------------------------------// +esp_err_t essl_spi_read_reg(void *arg, uint8_t addr, uint8_t *out_value, uint32_t wait_ms) +{ + essl_spi_context_t *ctx = arg; + ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first"); + uint8_t reserved_1_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_out.tx_sync_reg : ctx->master_in.rx_sync_reg; + uint8_t reserved_1_tail = reserved_1_head + 3; + uint8_t reserved_2_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_in.rx_sync_reg : ctx->master_out.tx_sync_reg; + uint8_t reserved_2_tail = reserved_2_head + 3; + ESP_RETURN_ON_FALSE(addr < reserved_1_head || (addr > reserved_1_tail && addr < reserved_2_head) || addr > reserved_2_tail, ESP_ERR_INVALID_ARG, TAG, "Invalid address"); + + return essl_spi_rdbuf(ctx->spi, out_value, addr, sizeof(uint8_t), 0); +} + +static uint32_t essl_spi_get_rx_data_size(void *arg) +{ + essl_spi_context_t *ctx = arg; + ESP_LOGV(TAG, "slave tx buffer: %d bytes, master has read: %d bytes", ctx->master_in.slave_tx_bytes, ctx->master_in.received_bytes); + return ctx->master_in.slave_tx_bytes - ctx->master_in.received_bytes; +} + +static esp_err_t essl_spi_update_rx_data_size(void *arg, uint32_t wait_ms) +{ + essl_spi_context_t *ctx = arg; + uint32_t updated_size = 0; + uint32_t previous_size = 0; + esp_err_t ret; + + ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&previous_size, ctx->master_in.rx_sync_reg, sizeof(uint32_t), 0); + if (ret != ESP_OK) { + return ret; + } + + /** + * Read until the last 2 reading result are same. Reason: + * SPI transaction is carried on per 1 Byte. So when Master is reading the shared register, if the + * register value is changed by Slave at this time, Master may get wrong data. + */ + while (1) { + ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&updated_size, ctx->master_in.rx_sync_reg, sizeof(uint32_t), 0); + if (ret != ESP_OK) { + return ret; + } + if (updated_size == previous_size) { + ctx->master_in.slave_tx_bytes = updated_size; + ESP_LOGV(TAG, "updated: slave prepared tx buffer is: %d bytes", (unsigned int)updated_size); + return ret; + } + previous_size = updated_size; + } +} + +esp_err_t essl_spi_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms) +{ + ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first"); + if (!esp_ptr_dma_capable(out_data) || ((intptr_t)out_data % 4) != 0) { + return ESP_ERR_INVALID_ARG; + } + + essl_spi_context_t *ctx = arg; + esp_err_t ret; + + if (essl_spi_get_rx_data_size(arg) < size) { + /** + * For realistic situation, usually there will be a large overhead (Slave will load large amount of data), + * so here we only update the Slave's TX size when the last-updated size is smaller than what Master requires. + */ + ret = essl_spi_update_rx_data_size(arg, wait_ms); + if (ret != ESP_OK) { + return ret; + } + + //Slave still did not load enough size of buffer + if (essl_spi_get_rx_data_size(arg) < size) { + ESP_LOGV(TAG, "slave buffer: %d is not enough, %d is required", ctx->master_in.slave_tx_bytes, ctx->master_in.received_bytes + size); + return ESP_ERR_NOT_FOUND; + } + } + + ESP_LOGV(TAG, "get_packet: size to read is: %d", size); + ret = essl_spi_rddma_seg(ctx->spi, out_data, size, 0); + if (ret != ESP_OK) { + return ret; + } + ctx->master_in.received_bytes += size; + + return ESP_OK; +} + +//------------------------------------ TX ----------------------------------// +esp_err_t essl_spi_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *out_value, uint32_t wait_ms) +{ + essl_spi_context_t *ctx = arg; + ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first"); + uint8_t reserved_1_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_out.tx_sync_reg : ctx->master_in.rx_sync_reg; + uint8_t reserved_1_tail = reserved_1_head + 3; + uint8_t reserved_2_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_in.rx_sync_reg : ctx->master_out.tx_sync_reg; + uint8_t reserved_2_tail = reserved_2_head + 3; + ESP_RETURN_ON_FALSE(addr < reserved_1_head || (addr > reserved_1_tail && addr < reserved_2_head) || addr > reserved_2_tail, ESP_ERR_INVALID_ARG, TAG, "Invalid address"); + ESP_RETURN_ON_FALSE(out_value == NULL, ESP_ERR_NOT_SUPPORTED, TAG, "This feature is not supported"); + + return essl_spi_wrbuf(ctx->spi, &value, addr, sizeof(uint8_t), 0); +} + +static uint32_t essl_spi_get_tx_buffer_num(void *arg) +{ + essl_spi_context_t *ctx = arg; + ESP_LOGV(TAG, "slave rx buffer: %d, master has sent: %d", ctx->master_out.slave_rx_buf_num, ctx->master_out.sent_buf_num); + return ctx->master_out.slave_rx_buf_num - ctx->master_out.sent_buf_num; +} + +static esp_err_t essl_spi_update_tx_buffer_num(void *arg, uint32_t wait_ms) +{ + essl_spi_context_t *ctx = arg; + uint32_t updated_num = 0; + uint32_t previous_size = 0; + esp_err_t ret; + + ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&previous_size, ctx->master_out.tx_sync_reg, sizeof(uint32_t), 0); + if (ret != ESP_OK) { + return ret; + } + + /** + * Read until the last 2 reading result are same. Reason: + * SPI transaction is carried on per 1 Byte. So when Master is reading the shared register, if the + * register value is changed by Slave at this time, Master may get wrong data. + */ + while (1) { + ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&updated_num, ctx->master_out.tx_sync_reg, sizeof(uint32_t), 0); + if (ret != ESP_OK) { + return ret; + } + if (updated_num == previous_size) { + ctx->master_out.slave_rx_buf_num = updated_num; + ESP_LOGV(TAG, "updated: slave prepared rx buffer: %d", (unsigned int)updated_num); + return ret; + } + previous_size = updated_num; + } +} + +esp_err_t essl_spi_send_packet(void *arg, const void *data, size_t size, uint32_t wait_ms) +{ + ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first"); + if (!esp_ptr_dma_capable(data)) { + return ESP_ERR_INVALID_ARG; + } + + essl_spi_context_t *ctx = arg; + esp_err_t ret; + uint32_t buf_num_to_use = (size + ctx->master_out.tx_buffer_size - 1) / ctx->master_out.tx_buffer_size; + + if (essl_spi_get_tx_buffer_num(arg) < buf_num_to_use) { + /** + * For realistic situation, usually there will be a large overhead (Slave will load enough number of RX buffers), + * so here we only update the Slave's RX buffer number when the last-updated number is smaller than what Master requires. + */ + ret = essl_spi_update_tx_buffer_num(arg, wait_ms); + if (ret != ESP_OK) { + return ret; + } + //Slave still did not load a sufficient amount of buffers + if (essl_spi_get_tx_buffer_num(arg) < buf_num_to_use) { + ESP_LOGV(TAG, "slave buffer: %"PRIu32" is not enough, %"PRIu32" is required", (uint32_t)ctx->master_out.slave_rx_buf_num, (uint32_t)ctx->master_out.sent_buf_num + buf_num_to_use); + return ESP_ERR_NOT_FOUND; + } + } + + ESP_LOGV(TAG, "send_packet: size to write is: %zu", size); + ret = essl_spi_wrdma_seg(ctx->spi, data, size, 0); + if (ret != ESP_OK) { + return ret; + } + ctx->master_out.sent_buf_num += buf_num_to_use; + + return essl_spi_wrdma_done(ctx->spi, 0); +} diff --git a/esp_serial_slave_link/idf_component.yml b/esp_serial_slave_link/idf_component.yml new file mode 100644 index 0000000..055f35d --- /dev/null +++ b/esp_serial_slave_link/idf_component.yml @@ -0,0 +1,5 @@ +version: "1.0.0" +description: "Espressif Serial Slave Link Library" +url: https://github.com/espressif/idf-extra-components/tree/master/esp_serial_slave_link +dependencies: + idf: ">=5.0" diff --git a/esp_serial_slave_link/include/esp_serial_slave_link/essl.h b/esp_serial_slave_link/include/esp_serial_slave_link/essl.h new file mode 100644 index 0000000..9afed86 --- /dev/null +++ b/esp_serial_slave_link/include/esp_serial_slave_link/essl.h @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +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 an 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 + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - Other value returned from lower layer `init`. + */ +esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms); + +/** Wait for interrupt of an ESSL slave device. + * + * @param handle Handle of an 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 + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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 ESSL slave. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK: Success + * - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode + * - One of the error codes from SDMMC/SPI host controller + */ +esp_err_t essl_get_tx_buffer_num(essl_handle_t handle, uint32_t *out_tx_num, uint32_t wait_ms); + +/** Get the size, in bytes, of the data that the ESSL slave is ready to send + * + * @param handle Handle of an ESSL device. + * @param out_rx_size Output of data size to read from slave, in bytes + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK: Success + * - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode + * - One of the error codes from SDMMC/SPI 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 an ESSL device. + * + * @return + * - ESP_OK: Success + * - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode + * - ESP_ERR_INVALID_ARG: Invalid argument, handle is not init. + */ +esp_err_t essl_reset_cnt(essl_handle_t handle); + +/** Send a packet to the ESSL Slave. The Slave receives the packet into buffers whose size is ``buffer_size`` (configured during initialization). + * + * @param handle Handle of an 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_INVALID_ARG: Invalid argument, handle is not init or other argument is not valid. + * - ESP_ERR_TIMEOUT: No buffer to use, or error ftrom SDMMC host controller. + * - ESP_ERR_NOT_FOUND: Slave is not ready for receiving. + * - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode + * - One of the error codes from SDMMC/SPI 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 ESSL slave. + * + * @param handle Handle of an 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 has been read from the slave. + * - ESP_ERR_INVALID_ARG: Invalid argument, The handle is not initialized or the other arguments are invalid. + * - ESP_ERR_NOT_FINISHED: Read was successful, but there is still data remaining. + * - ESP_ERR_NOT_FOUND: Slave is not ready to send data. + * - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode + * - One of the error codes from SDMMC/SPI 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 ESSL slave. + * + * @param handle Handle of an ESSL device. + * @param addr Address of register to write. For SDIO, valid address: 0-59. For SPI, see ``essl_spi.h`` + * @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 + * - One of the error codes from SDMMC/SPI 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 ESSL slave. + * + * @param handle Handle of a ``essl`` device. + * @param add Address of register to read. For SDIO, Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). For SPI, see ``essl_spi.h`` + * @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 + * - One of the error codes from SDMMC/SPI 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 an ESSL device. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @return + * - ESP_OK: If interrupt is triggered. + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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 ESSL slave. All the bits set in the mask will be cleared, while other bits will stay the same. + * + * @param handle Handle of an 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 + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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 ESSL slave. + * + * @param handle Handle of an 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. + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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 ESSL 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 an 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 + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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 ESSL slave. + * + * @param handle Handle of an 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 an 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 + * - ESP_ERR_NOT_SUPPORTED: Current device does not support this function. + * - 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); + +#ifdef __cplusplus +} +#endif diff --git a/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h b/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h new file mode 100644 index 0000000..1f26261 --- /dev/null +++ b/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h @@ -0,0 +1,247 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// ESP SDIO slave link used by the ESP host to communicate with ESP SDIO slave. + +#pragma once + +#include "esp_err.h" +#include "driver/sdmmc_host.h" + +#include "esp_serial_slave_link/essl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// 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 an ESSL SDIO 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 an ESSL SDIO 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 ESSL SDIO 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 ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 an ESSL SDIO 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 */ + +#ifdef __cplusplus +} +#endif diff --git a/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h b/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h new file mode 100644 index 0000000..ecf0531 --- /dev/null +++ b/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h @@ -0,0 +1,312 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" +#include "driver/spi_master.h" + +#include "esp_serial_slave_link/essl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Configuration of ESSL SPI device +typedef struct { + spi_device_handle_t *spi; ///< Pointer to SPI device handle. + uint32_t tx_buf_size; ///< The pre-negotiated Master TX buffer size used by both the host and the slave. + uint8_t tx_sync_reg; ///< The pre-negotiated register ID for Master-TX-SLAVE-RX synchronization. 1 word (4 Bytes) will be reserved for the synchronization. + uint8_t rx_sync_reg; ///< The pre-negotiated register ID for Master-RX-Slave-TX synchronization. 1 word (4 Bytes) will be reserved for the synchronization. +} essl_spi_config_t; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// APIs for DMA Append Mode +// This mode has a better performance for continuous Half Duplex SPI transactions. +// +// * You can use the ``essl_spi_init_dev`` and ``essl_spi_deinit_dev`` together with APIs in ``essl.h`` to communicate +// with ESP SPI Slaves in Half Duplex DMA Append Mode. See example for SPI SLAVE HALFDUPLEX APPEND MODE. +// * You can also use the following APIs to create your own logic. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @brief Initialize the ESSL SPI device function list and get its handle + * + * @param[out] out_handle Output of the handle + * @param init_config Configuration for the ESSL SPI device + * @return + * - ESP_OK: On success + * - ESP_ERR_NO_MEM: Memory exhausted + * - ESP_ERR_INVALID_STATE: SPI driver is not initialized + * - ESP_ERR_INVALID_ARG: Wrong register ID + */ +esp_err_t essl_spi_init_dev(essl_handle_t *out_handle, const essl_spi_config_t *init_config); + +/** + * @brief Deinitialize the ESSL SPI device and free the memory used by the device + * + * @param handle Handle of the ESSL SPI device + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_STATE: ESSL SPI is not in use + */ +esp_err_t essl_spi_deinit_dev(essl_handle_t handle); + +/** + * @brief Read from the shared registers + * + * @note The registers for Master/Slave synchronization are reserved. Do not use them. (see `rx_sync_reg` in `essl_spi_config_t`) + * + * @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``) + * @param addr Address of the shared registers. (Valid: 0 ~ SOC_SPI_MAXIMUM_BUFFER_SIZE, registers for M/S sync are reserved, see note1). + * @param[out] out_value Read buffer for the shared registers. + * @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0). + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized. + * - ESP_ERR_INVALID_ARG: The address argument is not valid. See note 1. + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_read_reg(void *arg, uint8_t addr, uint8_t *out_value, uint32_t wait_ms); + +/** + * @brief Get a packet from Slave + * + * @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``) + * @param[out] out_data Output data address + * @param size The size of the output data. + * @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0). + * @return + * - ESP_OK: On Success + * - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized. + * - ESP_ERR_INVALID_ARG: The output data address is neither DMA capable nor 4 byte-aligned + * - ESP_ERR_INVALID_SIZE: Master requires ``size`` bytes of data but Slave did not load enough bytes. + */ +esp_err_t essl_spi_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms); + +/** + * @brief Write to the shared registers + * + * @note The registers for Master/Slave synchronization are reserved. Do not use them. (see `tx_sync_reg` in `essl_spi_config_t`) + * @note Feature of checking the actual written value (``out_value``) is not supported. + * + * @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``) + * @param addr Address of the shared registers. (Valid: 0 ~ SOC_SPI_MAXIMUM_BUFFER_SIZE, registers for M/S sync are reserved, see note1) + * @param value Buffer for data to send, should be align to 4. + * @param[out] out_value Not supported, should be set to NULL. + * @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0). + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized. + * - ESP_ERR_INVALID_ARG: The address argument is not valid. See note 1. + * - ESP_ERR_NOT_SUPPORTED: Should set ``out_value`` to NULL. See note 2. + * - or other return value from :cpp:func:`spi_device_transmit`. + * + */ +esp_err_t essl_spi_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *out_value, uint32_t wait_ms); + +/** + * @brief Send a packet to Slave + * + * @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``) + * @param data Address of the data to send + * @param size Size of the data to send. + * @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0). + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized. + * - ESP_ERR_INVALID_ARG: The data address is not DMA capable + * - ESP_ERR_INVALID_SIZE: Master will send ``size`` bytes of data but Slave did not load enough RX buffer + */ +esp_err_t essl_spi_send_packet(void *arg, const void *data, size_t size, uint32_t wait_ms); + +/** + * @brief Reset the counter in Master context + * + * @note Shall only be called if the slave has reset its counter. Else, Slave and Master would be desynchronized + * + * @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``) + */ +void essl_spi_reset_cnt(void *arg); + +//////////////////////////////////////////////////////////////////////////////// +// Basic commands to communicate with the SPI Slave HD on ESP32-S2 +//////////////////////////////////////////////////////////////////////////////// +/** + * @brief Read the shared buffer from the slave in ISR way + * + * @note The slave's HW doesn't guarantee the data in one SPI transaction is consistent. It sends data in unit of byte. + * In other words, if the slave SW attempts to update the shared register when a rdbuf SPI transaction is in-flight, + * the data got by the master will be the combination of bytes of different writes of slave SW. + * + * @note ``out_data`` should be prepared in words and in the DRAM. The buffer may be written in words + * by the DMA. When a byte is written, the remaining bytes in the same word will also be + * overwritten, even the ``len`` is shorter than a word. + * + * @param spi SPI device handle representing the slave + * @param[out] out_data Buffer for read data, strongly suggested to be in the DRAM and aligned to 4 + * @param addr Address of the slave shared buffer + * @param len Length to read + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: on success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_rdbuf(spi_device_handle_t spi, uint8_t *out_data, int addr, int len, uint32_t flags); + +/** + * @brief Read the shared buffer from the slave in polling way + * + * @note ``out_data`` should be prepared in words and in the DRAM. The buffer may be written in words + * by the DMA. When a byte is written, the remaining bytes in the same word will also be + * overwritten, even the ``len`` is shorter than a word. + * + * @param spi SPI device handle representing the slave + * @param[out] out_data Buffer for read data, strongly suggested to be in the DRAM and aligned to 4 + * @param addr Address of the slave shared buffer + * @param len Length to read + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: on success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_rdbuf_polling(spi_device_handle_t spi, uint8_t *out_data, int addr, int len, uint32_t flags); + +/** + * @brief Write the shared buffer of the slave in ISR way + * + * @note ``out_data`` should be prepared in words and in the DRAM. The buffer may be written in words + * by the DMA. When a byte is written, the remaining bytes in the same word will also be + * overwritten, even the ``len`` is shorter than a word. + * + * @param spi SPI device handle representing the slave + * @param data Buffer for data to send, strongly suggested to be in the DRAM + * @param addr Address of the slave shared buffer, + * @param len Length to write + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_wrbuf(spi_device_handle_t spi, const uint8_t *data, int addr, int len, uint32_t flags); + +/** + * @brief Write the shared buffer of the slave in polling way + * + * @note ``out_data`` should be prepared in words and in the DRAM. The buffer may be written in words + * by the DMA. When a byte is written, the remaining bytes in the same word will also be + * overwritten, even the ``len`` is shorter than a word. + * + * @param spi SPI device handle representing the slave + * @param data Buffer for data to send, strongly suggested to be in the DRAM + * @param addr Address of the slave shared buffer, + * @param len Length to write + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_polling_transmit`. + */ +esp_err_t essl_spi_wrbuf_polling(spi_device_handle_t spi, const uint8_t *data, int addr, int len, uint32_t flags); + +/** + * @brief Receive long buffer in segments from the slave through its DMA. + * + * @note This function combines several :cpp:func:`essl_spi_rddma_seg` and one + * :cpp:func:`essl_spi_rddma_done` at the end. Used when the slave is working in segment mode. + * + * @param spi SPI device handle representing the slave + * @param[out] out_data Buffer to hold the received data, strongly suggested to be in the DRAM and aligned to 4 + * @param len Total length of data to receive. + * @param seg_len Length of each segment, which is not larger than the maximum transaction length + * allowed for the spi device. Suggested to be multiples of 4. When set < 0, means send + * all data in one segment (the ``rddma_done`` will still be sent.) + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_rddma(spi_device_handle_t spi, uint8_t *out_data, int len, int seg_len, uint32_t flags); + +/** + * @brief Read one data segment from the slave through its DMA. + * + * @note To read long buffer, call :cpp:func:`essl_spi_rddma` instead. + * + * @param spi SPI device handle representing the slave + * @param[out] out_data Buffer to hold the received data. strongly suggested to be in the DRAM and aligned to 4 + * @param seg_len Length of this segment + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_rddma_seg(spi_device_handle_t spi, uint8_t *out_data, int seg_len, uint32_t flags); + +/** + * @brief Send the ``rddma_done`` command to the slave. Upon receiving this command, the slave will + * stop sending the current buffer even there are data unsent, and maybe prepare the next buffer to + * send. + * + * @note This is required only when the slave is working in segment mode. + * + * @param spi SPI device handle representing the slave + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_rddma_done(spi_device_handle_t spi, uint32_t flags); + +/** + * @brief Send long buffer in segments to the slave through its DMA. + * + * @note This function combines several :cpp:func:`essl_spi_wrdma_seg` and one + * :cpp:func:`essl_spi_wrdma_done` at the end. Used when the slave is working in segment mode. + * + * @param spi SPI device handle representing the slave + * @param data Buffer for data to send, strongly suggested to be in the DRAM + * @param len Total length of data to send. + * @param seg_len Length of each segment, which is not larger than the maximum transaction length + * allowed for the spi device. Suggested to be multiples of 4. When set < 0, means send + * all data in one segment (the ``wrdma_done`` will still be sent.) + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_wrdma(spi_device_handle_t spi, const uint8_t *data, int len, int seg_len, uint32_t flags); + +/** + * @brief Send one data segment to the slave through its DMA. + * + * @note To send long buffer, call :cpp:func:`essl_spi_wrdma` instead. + * + * @param spi SPI device handle representing the slave + * @param data Buffer for data to send, strongly suggested to be in the DRAM + * @param seg_len Length of this segment + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_wrdma_seg(spi_device_handle_t spi, const uint8_t *data, int seg_len, uint32_t flags); + +/** + * @brief Send the ``wrdma_done`` command to the slave. Upon receiving this command, the slave will + * stop receiving, process the received data, and maybe prepare the next buffer to receive. + * + * @note This is required only when the slave is working in segment mode. + * + * @param spi SPI device handle representing the slave + * @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send. + * @return + * - ESP_OK: success + * - or other return value from :cpp:func:`spi_device_transmit`. + */ +esp_err_t essl_spi_wrdma_done(spi_device_handle_t spi, uint32_t flags); + +#ifdef __cplusplus +} +#endif diff --git a/esp_serial_slave_link/include/essl_spi/esp32c2_defs.h b/esp_serial_slave_link/include/essl_spi/esp32c2_defs.h new file mode 100644 index 0000000..0ef2000 --- /dev/null +++ b/esp_serial_slave_link/include/essl_spi/esp32c2_defs.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +// NOTE: From the view of master +#define CMD_HD_WRBUF_REG 0x01 +#define CMD_HD_RDBUF_REG 0x02 +#define CMD_HD_WRDMA_REG 0x03 +#define CMD_HD_RDDMA_REG 0x04 + +#define CMD_HD_ONEBIT_MODE 0x00 +#define CMD_HD_DOUT_MODE 0x10 +#define CMD_HD_QOUT_MODE 0x20 +#define CMD_HD_DIO_MODE 0x50 +#define CMD_HD_QIO_MODE 0xA0 + +#define CMD_HD_SEG_END_REG 0x05 +#define CMD_HD_EN_QPI_REG 0x06 +#define CMD_HD_WR_END_REG 0x07 +#define CMD_HD_INT0_REG 0x08 +#define CMD_HD_INT1_REG 0x09 +#define CMD_HD_INT2_REG 0x0A +#define CMD_HD_EX_QPI_REG 0xDD + +#define SPI_SLAVE_HD_BUFFER_SIZE 64 diff --git a/esp_serial_slave_link/include/essl_spi/esp32c3_defs.h b/esp_serial_slave_link/include/essl_spi/esp32c3_defs.h new file mode 100644 index 0000000..a237137 --- /dev/null +++ b/esp_serial_slave_link/include/essl_spi/esp32c3_defs.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// NOTE: From the view of master +#define CMD_HD_WRBUF_REG 0x01 +#define CMD_HD_RDBUF_REG 0x02 +#define CMD_HD_WRDMA_REG 0x03 +#define CMD_HD_RDDMA_REG 0x04 + +#define CMD_HD_ONEBIT_MODE 0x00 +#define CMD_HD_DOUT_MODE 0x10 +#define CMD_HD_QOUT_MODE 0x20 +#define CMD_HD_DIO_MODE 0x50 +#define CMD_HD_QIO_MODE 0xA0 + +#define CMD_HD_SEG_END_REG 0x05 +#define CMD_HD_EN_QPI_REG 0x06 +#define CMD_HD_WR_END_REG 0x07 +#define CMD_HD_INT0_REG 0x08 +#define CMD_HD_INT1_REG 0x09 +#define CMD_HD_INT2_REG 0x0A +#define CMD_HD_EX_QPI_REG 0xDD + +#define SPI_SLAVE_HD_BUFFER_SIZE 64 diff --git a/esp_serial_slave_link/include/essl_spi/esp32s2_defs.h b/esp_serial_slave_link/include/essl_spi/esp32s2_defs.h new file mode 100644 index 0000000..d5f872f --- /dev/null +++ b/esp_serial_slave_link/include/essl_spi/esp32s2_defs.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// NOTE: From the view of master +#define CMD_HD_WRBUF_REG 0x01 +#define CMD_HD_RDBUF_REG 0x02 +#define CMD_HD_WRDMA_REG 0x03 +#define CMD_HD_RDDMA_REG 0x04 + +#define CMD_HD_ONEBIT_MODE 0x00 +#define CMD_HD_DOUT_MODE 0x10 +#define CMD_HD_QOUT_MODE 0x20 +#define CMD_HD_DIO_MODE 0x50 +#define CMD_HD_QIO_MODE 0xA0 + +#define CMD_HD_SEG_END_REG 0x05 +#define CMD_HD_EN_QPI_REG 0x06 +#define CMD_HD_WR_END_REG 0x07 +#define CMD_HD_INT0_REG 0x08 +#define CMD_HD_INT1_REG 0x09 +#define CMD_HD_INT2_REG 0x0A +#define CMD_HD_EX_QPI_REG 0xDD + +#define SPI_SLAVE_HD_BUFFER_SIZE 72 diff --git a/esp_serial_slave_link/include/essl_spi/esp32s3_defs.h b/esp_serial_slave_link/include/essl_spi/esp32s3_defs.h new file mode 100644 index 0000000..a237137 --- /dev/null +++ b/esp_serial_slave_link/include/essl_spi/esp32s3_defs.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// NOTE: From the view of master +#define CMD_HD_WRBUF_REG 0x01 +#define CMD_HD_RDBUF_REG 0x02 +#define CMD_HD_WRDMA_REG 0x03 +#define CMD_HD_RDDMA_REG 0x04 + +#define CMD_HD_ONEBIT_MODE 0x00 +#define CMD_HD_DOUT_MODE 0x10 +#define CMD_HD_QOUT_MODE 0x20 +#define CMD_HD_DIO_MODE 0x50 +#define CMD_HD_QIO_MODE 0xA0 + +#define CMD_HD_SEG_END_REG 0x05 +#define CMD_HD_EN_QPI_REG 0x06 +#define CMD_HD_WR_END_REG 0x07 +#define CMD_HD_INT0_REG 0x08 +#define CMD_HD_INT1_REG 0x09 +#define CMD_HD_INT2_REG 0x0A +#define CMD_HD_EX_QPI_REG 0xDD + +#define SPI_SLAVE_HD_BUFFER_SIZE 64 diff --git a/test_app/CMakeLists.txt b/test_app/CMakeLists.txt index da92bbd..0083df6 100644 --- a/test_app/CMakeLists.txt +++ b/test_app/CMakeLists.txt @@ -14,7 +14,7 @@ endif() # 3. Add here if the component is compatible with IDF >= v5.0 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0") - list(APPEND EXTRA_COMPONENT_DIRS ../bdc_motor ../led_strip ../sh2lib ../nghttp) + list(APPEND EXTRA_COMPONENT_DIRS ../bdc_motor ../led_strip ../sh2lib ../nghttp ../esp_serial_slave_link) endif() # !This section should NOT be touched when adding new component!