diff --git a/components/eppp_link/CMakeLists.txt b/components/eppp_link/CMakeLists.txt index e7e9c1432e..eca2db0410 100644 --- a/components/eppp_link/CMakeLists.txt +++ b/components/eppp_link/CMakeLists.txt @@ -1,3 +1,13 @@ -idf_component_register(SRCS "eppp_link.c" +if(CONFIG_EPPP_LINK_USB_CDC_DEVICE) + set(transport_srcs eppp_usb_device.c) +elseif(CONFIG_EPPP_LINK_USB_CDC_HOST) + set(transport_srcs eppp_usb_host.c) +endif() + +idf_component_register(SRCS eppp_link.c eppp_sdio_slave.c eppp_sdio_host.c ${transport_srcs} INCLUDE_DIRS "include" PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver) + +if(CONFIG_EPPP_LINK_USB_CDC_DEVICE) + idf_component_optional_requires(PRIVATE espressif__esp_tinyusb) +endif() diff --git a/components/eppp_link/Kconfig b/components/eppp_link/Kconfig index 2376245794..c9c854a038 100644 --- a/components/eppp_link/Kconfig +++ b/components/eppp_link/Kconfig @@ -21,6 +21,19 @@ menu "eppp_link" bool "SPI" help Use SPI. + + config EPPP_LINK_DEVICE_SDIO + bool "SDIO" + depends on SOC_SDMMC_HOST_SUPPORTED || SOC_SDIO_SLAVE_SUPPORTED + help + Use SDIO. + + config EPPP_LINK_USB_CDC + bool "USB" + depends on SOC_USB_OTG_SUPPORTED + help + Use USB CDC. + endchoice config EPPP_LINK_CONN_MAX_RETRY @@ -34,8 +47,52 @@ menu "eppp_link" config EPPP_LINK_PACKET_QUEUE_SIZE int "Packet queue size" default 64 + depends on EPPP_LINK_DEVICE_SPI help Size of the Tx packet queue. You can decrease the number for slower bit rates. + choice EPPP_LINK_SDIO_ROLE + prompt "Choose SDIO host or slave" + depends on EPPP_LINK_DEVICE_SDIO + default EPPP_LINK_DEVICE_SDIO_HOST if SOC_SDMMC_HOST_SUPPORTED + help + Select which either SDIO host or slave + + config EPPP_LINK_DEVICE_SDIO_HOST + bool "Host" + depends on SOC_SDMMC_HOST_SUPPORTED + help + Use SDIO host. + + config EPPP_LINK_DEVICE_SDIO_SLAVE + bool "SLAVE" + depends on SOC_SDIO_SLAVE_SUPPORTED + help + Use SDIO slave. + + endchoice + + choice EPPP_LINK_USB_CDC_ROLE + prompt "Choose USB CDC device or host" + depends on EPPP_LINK_USB_CDC + default EPPP_LINK_USB_CDC_DEVICE + help + Choose the preferred USB role, either device or host. + + config EPPP_LINK_USB_CDC_HOST + bool "Host" + depends on EPPP_LINK_USB_CDC + help + Use USB CDC (ACM) host. + + config EPPP_LINK_USB_CDC_DEVICE + bool "Device" + select TINYUSB_CDC_ENABLED + depends on EPPP_LINK_USB_CDC + help + Use USB CDC device. + + endchoice + endmenu diff --git a/components/eppp_link/README.md b/components/eppp_link/README.md index 9b517e7f6a..4112c05919 100644 --- a/components/eppp_link/README.md +++ b/components/eppp_link/README.md @@ -14,7 +14,7 @@ brings in the WiFi connectivity from the "SLAVE" microcontroller. SLAVE micro HOST micro \|/ +----------------+ +----------------+ | | | serial line | | - +---+ WiFi NAT PPPoS |======== UART / SPI =======| PPPoS client | + +---+ WiFi NAT PPPoS |=== UART / SPI / SDIO =====| PPPoS client | | (server)| | | +----------------+ +----------------+ ``` @@ -39,14 +39,19 @@ brings in the WiFi connectivity from the "SLAVE" microcontroller. ## Throughput -Tested with WiFi-NAPT example, no IRAM optimizations +Tested with WiFi-NAPT example ### UART @ 3Mbauds * TCP - 2Mbits/s * UDP - 2Mbits/s -### SPI @ 20MHz +### SPI @ 16MHz -* TCP - 6Mbits/s -* UDP - 10Mbits/s +* TCP - 5Mbits/s +* UDP - 8Mbits/s + +### SDIO + +* TCP - 9Mbits/s +* UDP - 11Mbits/s diff --git a/components/eppp_link/eppp_link.c b/components/eppp_link/eppp_link.c index 2a763412e6..78d765b859 100644 --- a/components/eppp_link/eppp_link.c +++ b/components/eppp_link/eppp_link.c @@ -12,6 +12,7 @@ #include "esp_event.h" #include "esp_netif_ppp.h" #include "eppp_link.h" +#include "esp_serial_slave_link/essl_sdio.h" #if CONFIG_EPPP_LINK_DEVICE_SPI #include "driver/spi_master.h" @@ -38,6 +39,12 @@ struct packet { uint8_t *data; }; +#if CONFIG_EPPP_LINK_USB_CDC +#define EPPP_NEEDS_TASK 0 +#else +#define EPPP_NEEDS_TASK 1 +#endif + #if CONFIG_EPPP_LINK_DEVICE_SPI #define MAX_PAYLOAD 1500 #define MIN_TRIGGER_US 20 @@ -86,7 +93,22 @@ struct eppp_handle { bool netif_stop; }; - +typedef esp_err_t (*transmit_t)(void *h, void *buffer, size_t len); + +#if CONFIG_EPPP_LINK_DEVICE_SDIO +esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len); +esp_err_t eppp_sdio_host_rx(esp_netif_t *netif); +esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif); +esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len); +esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config); +esp_err_t eppp_sdio_slave_init(void); +void eppp_sdio_slave_deinit(void); +void eppp_sdio_host_deinit(void); +#elif CONFIG_EPPP_LINK_USB_CDC +esp_err_t eppp_transport_init(esp_netif_t *netif); +esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len); +void eppp_transport_deinit(void); +#else static esp_err_t transmit(void *h, void *buffer, size_t len) { struct eppp_handle *handle = h; @@ -125,9 +147,10 @@ static esp_err_t transmit(void *h, void *buffer, size_t len) #elif CONFIG_EPPP_LINK_DEVICE_UART ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_VERBOSE); uart_write_bytes(handle->uart_port, buffer, len); -#endif +#endif // DEVICE UART or SPI return ESP_OK; } +#endif static void netif_deinit(esp_netif_t *netif) { @@ -209,7 +232,13 @@ static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config) esp_netif_driver_ifconfig_t driver_cfg = { .handle = h, +#if CONFIG_EPPP_LINK_DEVICE_SDIO + .transmit = role == EPPP_CLIENT ? eppp_sdio_host_tx : eppp_sdio_slave_tx, +#elif CONFIG_EPPP_LINK_USB_CDC + .transmit = eppp_transport_tx, +#else .transmit = transmit, +#endif }; const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg; @@ -657,9 +686,24 @@ esp_err_t eppp_perform(esp_netif_t *netif) } return ESP_OK; } +#elif CONFIG_EPPP_LINK_DEVICE_SDIO + +esp_err_t eppp_perform(esp_netif_t *netif) +{ + struct eppp_handle *h = esp_netif_get_io_driver(netif); + if (h->stop) { + return ESP_ERR_TIMEOUT; + } + if (h->role == EPPP_SERVER) { + return eppp_sdio_slave_rx(netif); + } else { + return eppp_sdio_host_rx(netif); + } +} #endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART +#if EPPP_NEEDS_TASK static void ppp_task(void *args) { esp_netif_t *netif = args; @@ -668,6 +712,7 @@ static void ppp_task(void *args) h->exited = true; vTaskDelete(NULL); } +#endif static bool have_some_eppp_netif(esp_netif_t *netif, void *ctx) { @@ -700,6 +745,15 @@ void eppp_deinit(esp_netif_t *netif) } #elif CONFIG_EPPP_LINK_DEVICE_UART deinit_uart(esp_netif_get_io_driver(netif)); +#elif CONFIG_EPPP_LINK_DEVICE_SDIO + struct eppp_handle *h = esp_netif_get_io_driver(netif); + if (h->role == EPPP_CLIENT) { + eppp_sdio_host_deinit(); + } else { + eppp_sdio_slave_deinit(); + } +#elif CONFIG_EPPP_LINK_USB_CDC + eppp_transport_deinit(); #endif netif_deinit(netif); } @@ -714,7 +768,6 @@ esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config) esp_netif_t *netif = netif_init(role, config); if (!netif) { ESP_LOGE(TAG, "Failed to initialize PPP netif"); - remove_handlers(); return NULL; } esp_netif_ppp_config_t netif_params; @@ -732,6 +785,24 @@ esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config) } #elif CONFIG_EPPP_LINK_DEVICE_UART init_uart(esp_netif_get_io_driver(netif), config); +#elif CONFIG_EPPP_LINK_DEVICE_SDIO + esp_err_t ret; + if (role == EPPP_SERVER) { + ret = eppp_sdio_slave_init(); + } else { + ret = eppp_sdio_host_init(&config->sdio); + } + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret); + return NULL; + } +#elif CONFIG_EPPP_LINK_USB_CDC + esp_err_t ret = eppp_transport_init(netif); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize USB CDC driver %d", ret); + return NULL; + } #endif return netif; } @@ -754,6 +825,12 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time return NULL; } #endif +#if CONFIG_EPPP_LINK_DEVICE_SDIO + if (config->transport != EPPP_TRANSPORT_SDIO) { + ESP_LOGE(TAG, "Invalid transport: SDIO device must be enabled in Kconfig"); + return NULL; + } +#endif if (config->task.run_task == false) { ESP_LOGE(TAG, "task.run_task == false is invalid in this API. Please use eppp_init()"); @@ -780,11 +857,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time eppp_netif_start(netif); +#if EPPP_NEEDS_TASK if (xTaskCreate(ppp_task, "ppp connect", config->task.stack_size, netif, config->task.priority, NULL) != pdTRUE) { ESP_LOGE(TAG, "Failed to create a ppp connection task"); eppp_deinit(netif); return NULL; } +#endif int netif_cnt = get_netif_num(netif); if (netif_cnt < 0) { eppp_close(netif); diff --git a/components/eppp_link/eppp_sdio.h b/components/eppp_link/eppp_sdio.h new file mode 100644 index 0000000000..b6d1e4a69a --- /dev/null +++ b/components/eppp_link/eppp_sdio.h @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define MAX_SDIO_PAYLOAD 1500 +#define SDIO_ALIGN(size) (((size) + 3U) & ~(3U)) +#define SDIO_PAYLOAD SDIO_ALIGN(MAX_SDIO_PAYLOAD) +#define PPP_SOF 0x7E + +// Interrupts and registers +#define SLAVE_INTR 0 +#define SLAVE_REG_REQ 0 + +// Requests from host to slave +#define REQ_RESET 1 +#define REQ_INIT 2 diff --git a/components/eppp_link/eppp_sdio_host.c b/components/eppp_link/eppp_sdio_host.c new file mode 100644 index 0000000000..226d636424 --- /dev/null +++ b/components/eppp_link/eppp_sdio_host.c @@ -0,0 +1,201 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "driver/sdio_slave.h" +#include "esp_serial_slave_link/essl_sdio.h" +#include "eppp_sdio.h" +#include "driver/sdmmc_host.h" +#include "sdmmc_cmd.h" +#include "esp_check.h" +#include "eppp_link.h" + +#if CONFIG_EPPP_LINK_DEVICE_SDIO_HOST + +// For blocking operations +#define TIMEOUT_MAX UINT32_MAX +// Short timeout for sending/receiving ESSL packets +#define PACKET_TIMEOUT_MS 50 +// Used for padding unaligned packets, to simplify the logic and keep PPP protocol intact when padded + +static const char *TAG = "eppp_sdio_host"; +static SemaphoreHandle_t s_essl_mutex = NULL; +static essl_handle_t s_essl = NULL; +static sdmmc_card_t *s_card = NULL; + +static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PAYLOAD]; +static DMA_ATTR uint8_t rcv_buffer[SDIO_PAYLOAD]; + +esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len) +{ + if (s_essl == NULL || s_essl_mutex == NULL) { + // silently skip the Tx if the SDIO not fully initialized + return ESP_OK; + } + + memcpy(send_buffer, buffer, len); + size_t send_len = SDIO_ALIGN(len); + if (send_len > len) { + // pad with SOF's + memset(&send_buffer[len], PPP_SOF, send_len - len); + } + xSemaphoreTake(s_essl_mutex, portMAX_DELAY); + esp_err_t ret = essl_send_packet(s_essl, send_buffer, send_len, PACKET_TIMEOUT_MS); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Slave not ready to receive packet %x", ret); + vTaskDelay(pdMS_TO_TICKS(1000)); + ret = ESP_ERR_NO_MEM; // to inform the upper layers + } + ESP_LOG_BUFFER_HEXDUMP(TAG, send_buffer, send_len, ESP_LOG_VERBOSE); + xSemaphoreGive(s_essl_mutex); + return ret; +} + +static esp_err_t request_slave_reset(void) +{ + esp_err_t ret = ESP_OK; + ESP_LOGI(TAG, "send reset to slave..."); + ESP_GOTO_ON_ERROR(essl_write_reg(s_essl, SLAVE_REG_REQ, REQ_RESET, NULL, TIMEOUT_MAX), err, TAG, "write-reg failed"); + ESP_GOTO_ON_ERROR(essl_send_slave_intr(s_essl, BIT(SLAVE_INTR), TIMEOUT_MAX), err, TAG, "send-intr failed"); + vTaskDelay(pdMS_TO_TICKS(PACKET_TIMEOUT_MS)); + ESP_GOTO_ON_ERROR(essl_wait_for_ready(s_essl, TIMEOUT_MAX), err, TAG, "wait-for-ready failed"); + ESP_LOGI(TAG, "slave io ready"); +err: + return ret; +} + +esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *eppp_config) +{ + esp_err_t ret = ESP_OK; + + ESP_GOTO_ON_ERROR(sdmmc_host_init(), err, TAG, "sdmmc host init failed"); + + // configure SDIO interface and slot + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + slot_config.width = eppp_config->width; +#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX + slot_config.clk = eppp_config->clk; + slot_config.cmd = eppp_config->cmd; + slot_config.d0 = eppp_config->d0; + slot_config.d1 = eppp_config->d1; + slot_config.d2 = eppp_config->d2; + slot_config.d3 = eppp_config->d3; +#endif + + ESP_GOTO_ON_ERROR(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config), err, TAG, "init sdmmc host slot failed"); + + sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + config.flags = SDMMC_HOST_FLAG_4BIT; + config.flags |= SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF; + config.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + + s_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t)); + ESP_GOTO_ON_FALSE(s_card, ESP_ERR_NO_MEM, err, TAG, "card allocation failed"); + ESP_GOTO_ON_ERROR(sdmmc_card_init(&config, s_card), err, TAG, "sdmmc card init failed"); + + essl_sdio_config_t ser_config = { + .card = s_card, + .recv_buffer_size = SDIO_PAYLOAD, + }; + ESP_GOTO_ON_FALSE(essl_sdio_init_dev(&s_essl, &ser_config) == ESP_OK && s_essl, ESP_FAIL, err, TAG, "essl_sdio_init_dev failed"); + ESP_GOTO_ON_ERROR(essl_init(s_essl, TIMEOUT_MAX), err, TAG, "essl-init failed"); + ESP_GOTO_ON_ERROR(request_slave_reset(), err, TAG, "failed to reset the slave"); + ESP_GOTO_ON_FALSE((s_essl_mutex = xSemaphoreCreateMutex()), ESP_ERR_NO_MEM, err, TAG, "failed to create semaphore"); + return ret; + +err: + essl_sdio_deinit_dev(s_essl); + s_essl = NULL; + return ret; +} + +static esp_err_t get_intr(uint32_t *out_raw) +{ + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(essl_get_intr(s_essl, out_raw, NULL, 0), err, TAG, "essl-get-int failed"); + ESP_GOTO_ON_ERROR(essl_clear_intr(s_essl, *out_raw, 0), err, TAG, "essl-clear-int failed"); + ESP_LOGD(TAG, "intr: %08"PRIX32, *out_raw); +err: + return ret; +} + +esp_err_t eppp_sdio_host_rx(esp_netif_t *netif) +{ + uint32_t intr; + esp_err_t err = essl_wait_int(s_essl, TIMEOUT_MAX); + if (err == ESP_ERR_TIMEOUT) { + return ESP_OK; + } + xSemaphoreTake(s_essl_mutex, portMAX_DELAY); + err = get_intr(&intr); + if (err == ESP_ERR_TIMEOUT) { + xSemaphoreGive(s_essl_mutex); + return ESP_OK; + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to check for interrupts %d", err); + xSemaphoreGive(s_essl_mutex); + return ESP_FAIL; + } + if (intr & ESSL_SDIO_DEF_ESP32.new_packet_intr_mask) { + esp_err_t ret; + do { + size_t size_read = SDIO_PAYLOAD; + ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PAYLOAD, &size_read, PACKET_TIMEOUT_MS); + if (ret == ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "interrupt but no data can be read"); + break; + } else if (ret == ESP_OK) { + ESP_LOGD(TAG, "receive data, size: %d", size_read); + ESP_LOG_BUFFER_HEXDUMP(TAG, rcv_buffer, size_read, ESP_LOG_VERBOSE); + esp_netif_receive(netif, rcv_buffer, size_read, NULL); + break; + } else { + ESP_LOGE(TAG, "rx packet error: %08X", ret); + if (request_slave_reset() != ESP_OK) { + ESP_LOGE(TAG, "Failed to request slave reset %x", ret); + break; + } + } + } while (ret == ESP_ERR_NOT_FINISHED); + } + xSemaphoreGive(s_essl_mutex); + return ESP_OK; +} + +void eppp_sdio_host_deinit() +{ + essl_sdio_deinit_dev(s_essl); + sdmmc_host_deinit(); + free(s_card); + s_card = NULL; + s_essl = NULL; +} + +#else // SDMMC_HOST NOT-SUPPORTED + +esp_err_t eppp_sdio_host_tx(void *handle, void *buffer, size_t len) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t eppp_sdio_host_rx(esp_netif_t *netif) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +void eppp_sdio_host_deinit() +{ +} + +esp_err_t eppp_sdio_host_init() +{ + return ESP_ERR_NOT_SUPPORTED; +} +#endif // CONFIG_SOC_SDIO_SLAVE_SUPPORTED diff --git a/components/eppp_link/eppp_sdio_slave.c b/components/eppp_link/eppp_sdio_slave.c new file mode 100644 index 0000000000..aeb37ffb2c --- /dev/null +++ b/components/eppp_link/eppp_sdio_slave.c @@ -0,0 +1,174 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "driver/sdio_slave.h" +#include "eppp_sdio.h" +#include "esp_check.h" + +#if CONFIG_EPPP_LINK_DEVICE_SDIO_SLAVE +#define BUFFER_NUM 4 +#define BUFFER_SIZE SDIO_PAYLOAD +static const char *TAG = "eppp_sdio_slave"; +static DMA_ATTR uint8_t sdio_slave_rx_buffer[BUFFER_NUM][BUFFER_SIZE]; +static DMA_ATTR uint8_t sdio_slave_tx_buffer[SDIO_PAYLOAD]; +static int s_slave_request = 0; + +esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len) +{ + if (s_slave_request != REQ_INIT) { + // silently skip the Tx if the SDIO not fully initialized + return ESP_OK; + } + memcpy(sdio_slave_tx_buffer, buffer, len); + size_t send_len = SDIO_ALIGN(len); + if (send_len > len) { + // pad with SOF's if the size is not 4 bytes aligned + memset(&sdio_slave_tx_buffer[len], PPP_SOF, send_len - len); + } + + ESP_LOG_BUFFER_HEXDUMP(TAG, sdio_slave_tx_buffer, send_len, ESP_LOG_VERBOSE); + esp_err_t ret = sdio_slave_transmit(sdio_slave_tx_buffer, send_len); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "sdio slave transmit error, ret : 0x%x", ret); + // to inform the upper layers + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +static esp_err_t slave_reset(void) +{ + esp_err_t ret = ESP_OK; + ESP_LOGI(TAG, "SDIO slave reset"); + sdio_slave_stop(); + ESP_GOTO_ON_ERROR(sdio_slave_reset(), err, TAG, "slave reset failed"); + ESP_GOTO_ON_ERROR(sdio_slave_start(), err, TAG, "slave start failed"); + + while (1) { + sdio_slave_buf_handle_t handle; + ret = sdio_slave_send_get_finished(&handle, 0); + if (ret == ESP_ERR_TIMEOUT) { + break; + } + ESP_GOTO_ON_ERROR(ret, err, TAG, "slave-get-finished failed"); + ESP_GOTO_ON_ERROR(sdio_slave_recv_load_buf(handle), err, TAG, "slave-load-buf failed"); + } +err: + return ret; +} + +esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif) +{ + if (s_slave_request == REQ_RESET) { + ESP_LOGD(TAG, "request: %x", s_slave_request); + slave_reset(); + s_slave_request = REQ_INIT; + } + sdio_slave_buf_handle_t handle; + size_t length; + uint8_t *ptr; + esp_err_t ret = sdio_slave_recv_packet(&handle, pdMS_TO_TICKS(1000)); + if (ret == ESP_ERR_TIMEOUT) { + return ESP_OK; + } + if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) { +again: + ptr = sdio_slave_recv_get_buf(handle, &length); + esp_netif_receive(netif, ptr, length, NULL); + if (sdio_slave_recv_load_buf(handle) != ESP_OK) { + ESP_LOGE(TAG, "Failed to recycle packet buffer"); + return ESP_FAIL; + } + if (ret == ESP_ERR_NOT_FINISHED) { + ret = sdio_slave_recv_packet(&handle, 0); + if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) { + goto again; + } + } + ESP_LOG_BUFFER_HEXDUMP(TAG, ptr, length, ESP_LOG_VERBOSE); + return ESP_OK; + } + ESP_LOGE(TAG, "Error when receiving packet %d", ret); + return ESP_FAIL; +} + + +static void event_cb(uint8_t pos) +{ + ESP_EARLY_LOGI(TAG, "SDIO event: %d", pos); + if (pos == SLAVE_INTR) { + s_slave_request = sdio_slave_read_reg(SLAVE_REG_REQ); + sdio_slave_write_reg(SLAVE_REG_REQ, 0); + } +} + +esp_err_t eppp_sdio_slave_init(void) +{ + sdio_slave_config_t config = { + .sending_mode = SDIO_SLAVE_SEND_PACKET, + .send_queue_size = BUFFER_NUM, + .recv_buffer_size = BUFFER_SIZE, + .event_cb = event_cb, + }; + esp_err_t ret = sdio_slave_initialize(&config); + if (ret != ESP_OK) { + return ret; + } + + for (int i = 0; i < BUFFER_NUM; i++) { + sdio_slave_buf_handle_t handle = sdio_slave_recv_register_buf(sdio_slave_rx_buffer[i]); + if (handle == NULL) { + sdio_slave_deinit(); + return ESP_FAIL; + } + ret = sdio_slave_recv_load_buf(handle); + if (ret != ESP_OK) { + sdio_slave_deinit(); + return ESP_FAIL; + } + } + + sdio_slave_set_host_intena(SDIO_SLAVE_HOSTINT_SEND_NEW_PACKET); // only need one interrupt to notify of a new packet + + ret = sdio_slave_start(); + if (ret != ESP_OK) { + sdio_slave_deinit(); + return ESP_FAIL; + } + return ESP_OK; +} + +void eppp_sdio_slave_deinit(void) +{ + sdio_slave_stop(); + sdio_slave_deinit(); +} + +#else // SOC_SDIO_SLAVE NOT-SUPPORTED + +esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len) +{ + return ESP_ERR_NOT_SUPPORTED; +} +esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +void eppp_sdio_slave_deinit() +{ +} + +esp_err_t eppp_sdio_slave_init(void) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +#endif // CONFIG_SOC_SDIO_SLAVE_SUPPORTED diff --git a/components/eppp_link/eppp_usb_device.c b/components/eppp_link/eppp_usb_device.c new file mode 100644 index 0000000000..eb417eb471 --- /dev/null +++ b/components/eppp_link/eppp_usb_device.c @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "tinyusb.h" +#include "tusb_cdc_acm.h" +#include "esp_netif.h" + +static int s_itf; +static esp_netif_t *s_netif; +static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE]; +static const char *TAG = "eppp_usb_dev"; + +static void cdc_rx_callback(int itf, cdcacm_event_t *event) +{ + size_t rx_size = 0; + if (itf != s_itf) { + // Not our channel + return; + } + esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size); + if (ret == ESP_OK) { + ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_VERBOSE); + // pass the received data to the network interface + esp_netif_receive(s_netif, buf, rx_size, NULL); + } else { + ESP_LOGE(TAG, "Read error"); + } +} + +static void line_state_changed(int itf, cdcacm_event_t *event) +{ + s_itf = itf; // use this channel for the netif communication + ESP_LOGI(TAG, "Line state changed on channel %d", itf); +} + +esp_err_t eppp_transport_init(esp_netif_t *netif) +{ + ESP_LOGI(TAG, "USB initialization"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = NULL, + .string_descriptor = NULL, + .external_phy = false, + .configuration_descriptor = NULL, + }; + + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + + tinyusb_config_cdcacm_t acm_cfg = { + .usb_dev = TINYUSB_USBDEV_0, + .cdc_port = TINYUSB_CDC_ACM_0, + .callback_rx = &cdc_rx_callback, + .callback_rx_wanted_char = NULL, + .callback_line_state_changed = NULL, + .callback_line_coding_changed = NULL + }; + + ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg)); + /* the second way to register a callback */ + ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback( + TINYUSB_CDC_ACM_0, + CDC_EVENT_LINE_STATE_CHANGED, + &line_state_changed)); + s_netif = netif; + return ESP_OK; +} + +esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len) +{ + tinyusb_cdcacm_write_queue(s_itf, buffer, len); + tinyusb_cdcacm_write_flush(s_itf, 0); + return ESP_OK; +} + + +void eppp_transport_deinit(void) +{ + // tiny_usb deinit not supported yet +} diff --git a/components/eppp_link/eppp_usb_host.c b/components/eppp_link/eppp_usb_host.c new file mode 100644 index 0000000000..0c1ae8ca1f --- /dev/null +++ b/components/eppp_link/eppp_usb_host.c @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_netif.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "usb/usb_host.h" +#include "usb/cdc_acm_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (20) +#define EXAMPLE_USB_DEVICE_VID (0x303A) +#define EXAMPLE_USB_DEVICE_PID (0x4001) // 0x303A:0x4001 (TinyUSB CDC device) +#define EXAMPLE_TX_TIMEOUT_MS (200) + +static const char *TAG = "eppp_usb_host"; +static esp_netif_t *s_netif; +static cdc_acm_dev_hdl_t s_cdc_dev; + +static bool handle_rx(const uint8_t *data, size_t data_len, void *arg) +{ + ESP_LOGI(TAG, "Data received"); + ESP_LOG_BUFFER_HEXDUMP(TAG, data, data_len, ESP_LOG_VERBOSE); + // pass the received data to the network interface + esp_netif_receive(s_netif, (void *)data, data_len, NULL); + return true; +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case CDC_ACM_HOST_ERROR: + ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %i", event->data.error); + break; + case CDC_ACM_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(cdc_acm_host_close(event->data.cdc_hdl)); + break; + case CDC_ACM_HOST_SERIAL_STATE: + ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val); + break; + case CDC_ACM_HOST_NETWORK_CONNECTION: + default: + ESP_LOGW(TAG, "Unsupported CDC event: %i", event->type); + break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len) +{ + ESP_ERROR_CHECK(cdc_acm_host_data_tx_blocking(s_cdc_dev, (const uint8_t *)buffer, len, EXAMPLE_TX_TIMEOUT_MS)); + + return ESP_OK; +} + +esp_err_t eppp_transport_init(esp_netif_t *netif) +{ + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreate(usb_lib_task, "usb_lib", 4096, xTaskGetCurrentTaskHandle(), EXAMPLE_USB_HOST_PRIORITY, NULL); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing CDC-ACM driver"); + ESP_ERROR_CHECK(cdc_acm_host_install(NULL)); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 512, + .in_buffer_size = 512, + .user_arg = NULL, + .event_cb = handle_event, + .data_cb = handle_rx + }; + + while (true) { + // Open USB device from tusb_serial_device + ESP_LOGI(TAG, "Opening CDC ACM device 0x%04X:0x%04X...", EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID); + esp_err_t err = cdc_acm_host_open(EXAMPLE_USB_DEVICE_VID, EXAMPLE_USB_DEVICE_PID, 0, &dev_config, &s_cdc_dev); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + continue; + } + cdc_acm_host_desc_print(s_cdc_dev); + vTaskDelay(pdMS_TO_TICKS(100)); + s_netif = netif; + return ESP_OK; + } +} + +void eppp_transport_deinit(void) +{ + // usb host deinit not supported yet +} diff --git a/components/eppp_link/examples/host/main/app_main.c b/components/eppp_link/examples/host/main/app_main.c index 85ae186362..2651b4b1e9 100644 --- a/components/eppp_link/examples/host/main/app_main.c +++ b/components/eppp_link/examples/host/main/app_main.c @@ -104,11 +104,13 @@ void app_main(void) eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); #if CONFIG_EPPP_LINK_DEVICE_SPI config.transport = EPPP_TRANSPORT_SPI; -#else +#elif CONFIG_EPPP_LINK_DEVICE_UART config.transport = EPPP_TRANSPORT_UART; config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN; config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN; config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE; +#else + config.transport = EPPP_TRANSPORT_SDIO; #endif esp_netif_t *eppp_netif = eppp_connect(&config); if (eppp_netif == NULL) { diff --git a/components/eppp_link/examples/slave/main/eppp_slave.c b/components/eppp_link/examples/slave/main/eppp_slave.c index 3ad20ad7ba..2532b7ca1a 100644 --- a/components/eppp_link/examples/slave/main/eppp_slave.c +++ b/components/eppp_link/examples/slave/main/eppp_slave.c @@ -128,16 +128,23 @@ void app_main(void) ESP_ERROR_CHECK(ret); init_network_interface(); // WiFi station if withing SoC capabilities (otherwise a placeholder) +// ESP_ERROR_CHECK(esp_netif_init()); +// ESP_ERROR_CHECK(esp_event_loop_create_default()); eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG(); #if CONFIG_EPPP_LINK_DEVICE_SPI config.transport = EPPP_TRANSPORT_SPI; -#else +#elif CONFIG_EPPP_LINK_DEVICE_UART config.transport = EPPP_TRANSPORT_UART; config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN; config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN; config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE; -#endif +#elif CONFIG_EPPP_LINK_DEVICE_SDIO + config.transport = EPPP_TRANSPORT_SDIO; +#elif CONFIG_EPPP_LINK_USB_CDC + config.transport = EPPP_TRANSPORT_USB; +#endif // transport device + esp_netif_t *eppp_netif = eppp_listen(&config); if (eppp_netif == NULL) { ESP_LOGE(TAG, "Failed to setup connection"); diff --git a/components/eppp_link/idf_component.yml b/components/eppp_link/idf_component.yml index d6ac84bf2c..a0f1b71691 100644 --- a/components/eppp_link/idf_component.yml +++ b/components/eppp_link/idf_component.yml @@ -2,5 +2,7 @@ version: 0.1.1 url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router dependencies: - idf: - version: '>=5.2' + idf: '>=5.2' + espressif/esp_serial_slave_link: "^1.1.0" + espressif/esp_tinyusb: "^1" + usb_host_cdc_acm: "2.*" diff --git a/components/eppp_link/include/eppp_link.h b/components/eppp_link/include/eppp_link.h index 289585270a..0eee1c81b5 100644 --- a/components/eppp_link/include/eppp_link.h +++ b/components/eppp_link/include/eppp_link.h @@ -28,7 +28,16 @@ .rx_io = 26, \ .queue_size = 16, \ .rx_buffer_size = 1024, \ - }, \ + }, \ + .sdio = { \ + .width = 4, \ + .clk = 18, \ + .cmd = 19, \ + .d0 = 49, \ + .d1 = 50, \ + .d2 = 16, \ + .d3 = 17, \ + }, \ . task = { \ .run_task = true, \ .stack_size = 4096, \ @@ -53,6 +62,8 @@ typedef enum eppp_type { typedef enum eppp_transport { EPPP_TRANSPORT_UART, EPPP_TRANSPORT_SPI, + EPPP_TRANSPORT_SDIO, + EPPP_TRANSPORT_USB, } eppp_transport_t; @@ -81,6 +92,16 @@ typedef struct eppp_config_t { int rx_buffer_size; } uart; + struct eppp_config_sdio_s { + int width; + int clk; + int cmd; + int d0; + int d1; + int d2; + int d3; + } sdio; + struct eppp_config_task_s { bool run_task; int stack_size;