diff --git a/.github/workflows/eppp__build.yml b/.github/workflows/eppp__build.yml index a8e9acd82..097faed93 100644 --- a/.github/workflows/eppp__build.yml +++ b/.github/workflows/eppp__build.yml @@ -13,7 +13,7 @@ jobs: name: Build strategy: matrix: - idf_ver: ["latest", "release-v5.3"] + idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3"] test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }] runs-on: ubuntu-22.04 container: espressif/idf:${{ matrix.idf_ver }} @@ -21,6 +21,8 @@ jobs: - name: Checkout esp-protocols uses: actions/checkout@v3 - name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} + env: + EXPECTED_WARNING: "DeprecationWarning: 'MultiCommand' is deprecated and will be removed\nCryptographyDeprecationWarning: Parsed a serial number which wasn't positive" shell: bash run: | . ${IDF_PATH}/export.sh diff --git a/components/eppp_link/CMakeLists.txt b/components/eppp_link/CMakeLists.txt index 72b0ee19f..71d01d8ea 100644 --- a/components/eppp_link/CMakeLists.txt +++ b/components/eppp_link/CMakeLists.txt @@ -8,15 +8,26 @@ if(CONFIG_EPPP_LINK_DEVICE_ETH) set(transport_src eppp_eth.c) endif() -if(CONFIG_EPPP_LINK_DEVICE_SDIO) - set(transport_src eppp_sdio_slave.c eppp_sdio_host.c) +if(CONFIG_EPPP_LINK_DEVICE_SPI) + set(transport_src eppp_spi.c) endif() -idf_component_register(SRCS eppp_link.c ${transport_src} +if(CONFIG_EPPP_LINK_DEVICE_UART) + set(transport_src eppp_uart.c) +endif() + +if(CONFIG_EPPP_LINK_DEVICE_SDIO) + set(transport_src eppp_sdio.c eppp_sdio_slave.c eppp_sdio_host.c) +endif() + +if(NOT CONFIG_EPPP_LINK_USES_PPP) + set(netif_src eppp_netif_tun.c) +endif() + +idf_component_register(SRCS eppp_link.c ${transport_src} ${netif_src} INCLUDE_DIRS "include" PRIV_REQUIRES esp_netif esp_timer esp_eth ${driver_deps}) if(CONFIG_EPPP_LINK_DEVICE_ETH) - idf_component_get_property(ethernet_init espressif__ethernet_init COMPONENT_LIB) - target_link_libraries(${COMPONENT_LIB} PRIVATE ${ethernet_init}) + idf_component_optional_requires(PRIVATE ethernet_init espressif__ethernet_init) endif() diff --git a/components/eppp_link/Kconfig b/components/eppp_link/Kconfig index 3cf2604d1..b8ede693e 100644 --- a/components/eppp_link/Kconfig +++ b/components/eppp_link/Kconfig @@ -1,10 +1,16 @@ menu "eppp_link" - config EPPP_LINK_USES_LWIP - bool - default "y" + config EPPP_LINK_USES_PPP + bool "Use PPP network interface" + default "n" select LWIP_PPP_SUPPORT select LWIP_PPP_SERVER_SUPPORT + help + Enable this option to use PPP network interface. + This is useful when pairing with another PPP device, + e.g. pppd service on Linux. + By default EPPP_LINK uses plain TUN interface, + relying on transports to split on packet boundaries. choice EPPP_LINK_DEVICE prompt "Choose PPP device" diff --git a/components/eppp_link/README.md b/components/eppp_link/README.md index 8d2d8163f..5ce78c3c6 100644 --- a/components/eppp_link/README.md +++ b/components/eppp_link/README.md @@ -1,17 +1,19 @@ # ESP PPP Link component (eppp_link) -The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server (slave), the other one as PPP client (host). -This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi +The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server, the other one as PPP client. +This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol (if enabled) to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi. +Uses simplified TUN network interface by default to enable faster data transfer on non-UART transports. ## Typical application Using this component we can construct a WiFi connectivity gateway on PPP channel. The below diagram depicts an application where PPP server is running on a WiFi capable chip with NAPT module translating packets between WiFi and PPPoS interface. -We usually call this node a SLAVE microcontroller. The "HOST" microcontroller runs PPP client and connects only to the serial line, -brings in the WiFi connectivity from the "SLAVE" microcontroller. +We usually call this node a communication coprocessor, or a "SLAVE" microcontroller. +The main microcontroller (sometimes also called the "HOST") runs PPP client and connects only to the serial line, +brings in the WiFi connectivity from the communication coprocessor. ``` - SLAVE micro HOST micro + Communication coprocessor Main microcontroller \|/ +----------------+ +----------------+ | | | (serial) line | | +---+ WiFi NAT PPPoS |=== UART / SPI / SDIO / ETH ===| PPPoS client | @@ -19,6 +21,25 @@ brings in the WiFi connectivity from the "SLAVE" microcontroller. +----------------+ +----------------+ ``` +## Configuration + +### Choose the transport layer + +Use `idf.py menuconfig` to select the transport layer: + +* `CONFIG_EPPP_LINK_UART` -- Use UART transport layer +* `CONFIG_EPPP_LINK_SPI` -- Use SPI transport layer +* `CONFIG_EPPP_LINK_SDIO` -- Use SDIO transport layer +* `CONFIG_EPPP_LINK_ETHERNET` -- Use Ethernet transport + - Note: Ethernet creates it's own task, so calling `eppp_perform()` would not work + - Note: Add dependency to ethernet_init component to use other Ethernet drivers + - Note: You can override functions `eppp_transport_ethernet_deinit()` and `eppp_transport_ethernet_init()` to use your own Ethernet driver + +### Choose the network interface + +Use PPP netif for UART; Keep the default (TUN) for others + + ## API ### Client diff --git a/components/eppp_link/eppp_eth.c b/components/eppp_link/eppp_eth.c index fdfe02394..1d698ba1f 100644 --- a/components/eppp_link/eppp_eth.c +++ b/components/eppp_link/eppp_eth.c @@ -11,11 +11,18 @@ #include "esp_check.h" #include "esp_event.h" #include "esp_mac.h" +#include "esp_idf_version.h" #include "eppp_link.h" #include "eppp_transport.h" #include "esp_eth_driver.h" -#include "ethernet_init.h" #include "esp_eth_spec.h" +#include "eppp_transport_eth.h" +// Use Ethernet Init component if available +// (otherwise use just simple init/deinit with generic MAC/PHY) +#if __has_include("ethernet_init.h") +#define USE_ETHERNET_INIT_COMPONENT +#include "ethernet_init.h" +#endif typedef struct header { uint8_t dst[ETH_ADDR_LEN]; @@ -26,10 +33,57 @@ typedef struct header { static const char *TAG = "eppp_ethernet"; static bool s_is_connected = false; static esp_eth_handle_t *s_eth_handles = NULL; -static uint8_t s_out_buffer[ETH_MAX_PACKET_SIZE]; static uint8_t s_their_mac[ETH_ADDR_LEN]; static uint8_t s_our_mac[ETH_ADDR_LEN]; +#ifndef USE_ETHERNET_INIT_COMPONENT +static esp_eth_handle_t s_handles[1] = { NULL }; +static esp_eth_mac_t *s_mac = NULL; +static esp_eth_phy_t *s_phy = NULL; + +static void simple_deinit(esp_eth_handle_t *handle_array[]) +{ + if (s_handles[0] != NULL) { + esp_eth_driver_uninstall(s_handles[0]); + s_handles[0] = NULL; + } + if (s_mac != NULL) { + s_mac->del(s_mac); + s_mac = NULL; + } + if (s_phy != NULL) { + s_phy->del(s_phy); + s_phy = NULL; + } +} + +static esp_err_t simple_init(struct eppp_config_ethernet_s *config, esp_eth_handle_t *handle_array[]) +{ + esp_err_t ret = ESP_OK; + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); + esp32_emac_config.smi_gpio.mdc_num = config->mdc_io; + esp32_emac_config.smi_gpio.mdio_num = config->mdio_io; + s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + phy_config.phy_addr = config->phy_addr; + phy_config.reset_gpio_num = config->rst_io; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + s_phy = esp_eth_phy_new_generic(&phy_config); +#else + s_phy = esp_eth_phy_new_ip101(&phy_config); +#endif + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(s_mac, s_phy); + ESP_GOTO_ON_ERROR(esp_eth_driver_install(ð_config, &s_handles[0]), err, TAG, "Ethernet driver install failed"); + *handle_array = s_handles; + return ESP_OK; +err: + simple_deinit(handle_array); + return ret; +} + +#endif // USE_ETHERNET_INIT_COMPONENT + static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -65,48 +119,35 @@ static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void return ESP_FAIL; } -__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle_array[]) +__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(struct eppp_config_ethernet_s *config, esp_eth_handle_t *handle_array[]) { +#ifdef USE_ETHERNET_INIT_COMPONENT uint8_t eth_port_cnt = 0; ESP_RETURN_ON_ERROR(ethernet_init_all(handle_array, ð_port_cnt), TAG, "Failed to init common eth drivers"); ESP_RETURN_ON_FALSE(eth_port_cnt == 1, ESP_ERR_INVALID_ARG, TAG, "multiple Ethernet devices detected, please init only one"); return ESP_OK; +#else + return simple_init(config, handle_array); +#endif } -__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array) +__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array[]) { +#ifdef USE_ETHERNET_INIT_COMPONENT ethernet_deinit_all(s_eth_handles); -} - - -esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif) -{ - ESP_RETURN_ON_ERROR(eppp_transport_ethernet_init(&s_eth_handles), TAG, "Failed to initialize Ethernet driver"); - ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback"); - sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8, - &s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]); - - sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8, - &s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]); - esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac); - ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers"); - ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver"); - return ESP_OK; -} - -void eppp_transport_deinit(void) -{ - esp_eth_stop(s_eth_handles[0]); - eppp_transport_ethernet_deinit(s_eth_handles); +#else + simple_deinit(handle_array); +#endif } esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len) { + static uint8_t out_buffer[ETH_HEADER_LEN]; if (!s_is_connected) { return ESP_FAIL; } // setup Ethernet header - header_t *head = (header_t *)s_out_buffer; + header_t *head = (header_t *)out_buffer; memcpy(head->dst, s_their_mac, ETH_ADDR_LEN); memcpy(head->src, s_our_mac, ETH_ADDR_LEN); head->len = len; @@ -114,6 +155,56 @@ esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len) if (len > ETH_MAX_PAYLOAD_LEN) { return ESP_FAIL; } - memcpy(s_out_buffer + ETH_HEADER_LEN, buffer, len); - return esp_eth_transmit(s_eth_handles[0], s_out_buffer, len + ETH_HEADER_LEN); + return esp_eth_transmit_vargs(s_eth_handles[0], 2, out_buffer, ETH_HEADER_LEN, buffer, len); +} + +static esp_err_t start_driver(esp_netif_t *esp_netif) +{ + ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback"); + sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8, + &s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]); + + sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8, + &s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]); + esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac); + ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers"); + ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver"); + return ESP_OK; +} + +static esp_err_t post_attach(esp_netif_t *esp_netif, void *args) +{ + eppp_transport_handle_t h = (eppp_transport_handle_t)args; + ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null"); + h->base.netif = esp_netif; + + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = h, + .transmit = eppp_transport_tx, + }; + + ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config"); + ESP_LOGI(TAG, "EPPP Ethernet transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif)); + ESP_RETURN_ON_ERROR(start_driver(esp_netif), TAG, "Failed to start EPPP ethernet driver"); + ESP_LOGI(TAG, "EPPP Ethernet driver started"); + return ESP_OK; +} + +eppp_transport_handle_t eppp_eth_init(struct eppp_config_ethernet_s *config) +{ + __attribute__((unused)) esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null"); + eppp_transport_handle_t h = calloc(1, sizeof(struct eppp_handle)); + ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle"); + ESP_GOTO_ON_ERROR(eppp_transport_ethernet_init(config, &s_eth_handles), err, TAG, "Failed to init Ethernet transport"); + h->base.post_attach = post_attach; + return h; +err: + return NULL; +} + +void eppp_eth_deinit(eppp_transport_handle_t h) +{ + esp_eth_stop(s_eth_handles[0]); + eppp_transport_ethernet_deinit(&s_eth_handles); } diff --git a/components/eppp_link/eppp_link.c b/components/eppp_link/eppp_link.c index bf76ba4eb..77c2a1805 100644 --- a/components/eppp_link/eppp_link.c +++ b/components/eppp_link/eppp_link.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,17 +12,12 @@ #include "esp_event.h" #include "esp_netif_ppp.h" #include "eppp_link.h" +#include "eppp_transport_eth.h" +#include "eppp_transport_spi.h" +#include "eppp_transport_uart.h" +#include "eppp_transport_sdio.h" #include "eppp_transport.h" -#if CONFIG_EPPP_LINK_DEVICE_SPI -#include "driver/spi_master.h" -#include "driver/spi_slave.h" -#include "driver/gpio.h" -#include "esp_timer.h" -#include "esp_rom_crc.h" -#elif CONFIG_EPPP_LINK_DEVICE_UART -#include "driver/uart.h" -#endif #if CONFIG_EPPP_LINK_DEVICE_ETH #define EPPP_NEEDS_TASK 0 @@ -39,208 +34,54 @@ static const char *TAG = "eppp_link"; static int s_retry_num = 0; static int s_eppp_netif_count = 0; // used as a suffix for the netif key - -struct packet { - size_t len; - uint8_t *data; -}; - -#if CONFIG_EPPP_LINK_DEVICE_SPI -#define MAX_PAYLOAD 1500 -#define MIN_TRIGGER_US 20 -#define SPI_HEADER_MAGIC 0x1234 - -static void timer_callback(void *arg); - -struct header { - uint16_t magic; - uint16_t size; - uint16_t next_size; - uint16_t check; -} __attribute__((packed)); - -enum blocked_status { - NONE, - MASTER_BLOCKED, - MASTER_WANTS_READ, - SLAVE_BLOCKED, - SLAVE_WANTS_WRITE, -}; - -#endif // CONFIG_EPPP_LINK_DEVICE_SPI - -struct eppp_handle { -#if CONFIG_EPPP_LINK_DEVICE_SPI - QueueHandle_t out_queue; - QueueHandle_t ready_semaphore; - spi_device_handle_t spi_device; - spi_host_device_t spi_host; - int gpio_intr; - uint16_t next_size; - uint16_t transaction_size; - struct packet outbound; - enum blocked_status blocked; - uint32_t slave_last_edge; - esp_timer_handle_t timer; -#elif CONFIG_EPPP_LINK_DEVICE_UART - QueueHandle_t uart_event_queue; - uart_port_t uart_port; -#endif - esp_netif_t *netif; - eppp_type_t role; - bool stop; - bool exited; - 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_DEVICE_ETH - -#else -static esp_err_t transmit(void *h, void *buffer, size_t len) -{ - struct eppp_handle *handle = h; -#if CONFIG_EPPP_LINK_DEVICE_SPI - struct packet buf = { }; - uint8_t *current_buffer = buffer; - size_t remaining = len; - do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform - // fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD) - size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining; - buf.data = malloc(batch); - if (buf.data == NULL) { - ESP_LOGE(TAG, "Failed to allocate packet"); - return ESP_ERR_NO_MEM; - } - buf.len = batch; - remaining -= batch; - memcpy(buf.data, current_buffer, batch); - current_buffer += batch; - BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0); - if (ret != pdTRUE) { - ESP_LOGE(TAG, "Failed to queue packet to slave!"); - return ESP_ERR_NO_MEM; - } - } while (remaining > 0); - - if (handle->role == EPPP_SERVER && handle->blocked == SLAVE_BLOCKED) { - uint32_t now = esp_timer_get_time(); - uint32_t diff = now - handle->slave_last_edge; - if (diff < MIN_TRIGGER_US) { - esp_rom_delay_us(MIN_TRIGGER_US - diff); - } - gpio_set_level(handle->gpio_intr, 0); - } - -#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 // DEVICE UART or SPI - return ESP_OK; -} -#endif - -static void netif_deinit(esp_netif_t *netif) +void eppp_netif_deinit(esp_netif_t *netif) { if (netif == NULL) { return; } - struct eppp_handle *h = esp_netif_get_io_driver(netif); - if (h == NULL) { - return; - } -#if CONFIG_EPPP_LINK_DEVICE_SPI - struct packet buf = { }; - while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) { - if (buf.len > 0) { - free(buf.data); - } - } - vQueueDelete(h->out_queue); - if (h->role == EPPP_CLIENT) { - vSemaphoreDelete(h->ready_semaphore); - } -#endif - free(h); esp_netif_destroy(netif); if (s_eppp_netif_count > 0) { s_eppp_netif_count--; } } -static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config) +#ifdef CONFIG_EPPP_LINK_USES_PPP +#define NETSTACK_CONFIG() ESP_NETIF_NETSTACK_DEFAULT_PPP +#else +#define NETSTACK_CONFIG() g_eppp_netif_config_tun +extern esp_netif_netstack_config_t *g_eppp_netif_config_tun; +#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \ + { \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \ + .flags = ESP_NETIF_FLAG_EVENT_IP_MODIFIED, \ + .get_ip_event = IP_EVENT_PPP_GOT_IP, \ + .lost_ip_event = IP_EVENT_PPP_LOST_IP, \ + .if_key = "EPPP_TUN", \ + .if_desc = "eppp", \ + .route_prio = 1, \ + .bridge_info = NULL \ +}; +#endif // CONFIG_EPPP_LINK_USES_PPP + +esp_netif_t *eppp_netif_init(eppp_type_t role, eppp_transport_handle_t h, eppp_config_t *eppp_config) { if (s_eppp_netif_count > 9) { // Limit to max 10 netifs, since we use "EPPPx" as the unique key (where x is 0-9) ESP_LOGE(TAG, "Cannot create more than 10 instances"); return NULL; } - // Create the object first - struct eppp_handle *h = calloc(1, sizeof(struct eppp_handle)); - if (!h) { - ESP_LOGE(TAG, "Failed to allocate eppp_handle"); - return NULL; - } h->role = role; -#if CONFIG_EPPP_LINK_DEVICE_SPI - h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet)); - if (!h->out_queue) { - ESP_LOGE(TAG, "Failed to create the packet queue"); - free(h); - return NULL; - } - if (role == EPPP_CLIENT) { - h->ready_semaphore = xSemaphoreCreateBinary(); - if (!h->ready_semaphore) { - ESP_LOGE(TAG, "Failed to create the packet queue"); - vQueueDelete(h->out_queue); - free(h); - return NULL; - } - } - h->transaction_size = 0; - h->outbound.data = NULL; - h->outbound.len = 0; - if (role == EPPP_SERVER) { - esp_timer_create_args_t args = { - .callback = &timer_callback, - .arg = h, - .name = "timer" - }; - if (esp_timer_create(&args, &h->timer) != ESP_OK) { - ESP_LOGE(TAG, "Failed to create the packet queue"); - vQueueDelete(h->out_queue); - vSemaphoreDelete(h->ready_semaphore); - free(h); - return NULL; - } - } - -#endif - - 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_DEVICE_ETH - .transmit = eppp_transport_tx, -#else - .transmit = transmit, -#endif - }; - const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg; - +#ifdef CONFIG_EPPP_LINK_USES_PPP esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP(); +#else + esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_SLIP(); + esp_netif_ip_info_t slip_ip4 = {}; + slip_ip4.ip.addr = eppp_config->ppp.our_ip4_addr.addr; + slip_ip4.gw.addr = eppp_config->ppp.their_ip4_addr.addr; + slip_ip4.netmask.addr = ESP_IP4TOADDR(255, 255, 255, 0); + base_netif_cfg.ip_info = &slip_ip4; +#endif char if_key[] = "EPPP0"; // netif key needs to be unique if_key[sizeof(if_key) - 2 /* 2 = two chars before the terminator */ ] += s_eppp_netif_count++; base_netif_cfg.if_key = if_key; @@ -252,25 +93,27 @@ static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config) if (eppp_config->ppp.netif_prio) { base_netif_cfg.route_prio = eppp_config->ppp.netif_prio; } - esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg, - .driver = ppp_driver_cfg, - .stack = ESP_NETIF_NETSTACK_DEFAULT_PPP - }; + esp_netif_config_t netif_config = { .base = &base_netif_cfg, + .driver = NULL, + .stack = NETSTACK_CONFIG(), + }; - esp_netif_t *netif = esp_netif_new(&netif_ppp_config); - if (!netif) { - ESP_LOGE(TAG, "Failed to create esp_netif"); -#if CONFIG_EPPP_LINK_DEVICE_SPI - vQueueDelete(h->out_queue); - if (h->ready_semaphore) { - vSemaphoreDelete(h->ready_semaphore); - } -#endif - free(h); - return NULL; - } +#ifdef CONFIG_EPPP_LINK_USES_PPP + __attribute__((unused)) esp_err_t ret = ESP_OK; + esp_netif_t *netif = esp_netif_new(&netif_config); + esp_netif_ppp_config_t netif_params; + ESP_GOTO_ON_ERROR(esp_netif_ppp_get_params(netif, &netif_params), err, TAG, "Failed to get PPP params"); + netif_params.ppp_our_ip4_addr = eppp_config->ppp.our_ip4_addr; + netif_params.ppp_their_ip4_addr = eppp_config->ppp.their_ip4_addr; + netif_params.ppp_error_event_enabled = true; + ESP_GOTO_ON_ERROR(esp_netif_ppp_set_params(netif, &netif_params), err, TAG, "Failed to set PPP params"); return netif; - +err: + esp_netif_destroy(netif); + return NULL; +#else + return esp_netif_new(&netif_config); +#endif // CONFIG_EPPP_LINK_USES_PPP } esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms) @@ -295,7 +138,12 @@ esp_err_t eppp_netif_start(esp_netif_t *netif) { esp_netif_action_start(netif, 0, 0, 0); esp_netif_action_connected(netif, 0, 0, 0); +#ifndef CONFIG_EPPP_LINK_USES_PPP + // PPP provides address negotiation, if not PPP, we need to check connection manually + return eppp_check_connection(netif); +#else return ESP_OK; +#endif } static int get_netif_num(esp_netif_t *netif) @@ -318,7 +166,6 @@ static int get_netif_num(esp_netif_t *netif) static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data) { esp_netif_t **netif = data; - ESP_LOGD(TAG, "PPP status event: %" PRId32, event_id); if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) { ESP_LOGI(TAG, "Disconnected %d", get_netif_num(*netif)); struct eppp_handle *h = esp_netif_get_io_driver(*netif); @@ -351,356 +198,6 @@ static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void } } -#if CONFIG_EPPP_LINK_DEVICE_SPI - -#define SPI_ALIGN(size) (((size) + 3U) & ~(3U)) -#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6)) -#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */ - -static void IRAM_ATTR timer_callback(void *arg) -{ - struct eppp_handle *h = arg; - if (h->blocked == SLAVE_WANTS_WRITE) { - gpio_set_level(h->gpio_intr, 0); - } -} - -static void IRAM_ATTR gpio_isr_handler(void *arg) -{ - static uint32_t s_last_time; - uint32_t now = esp_timer_get_time(); - uint32_t diff = now - s_last_time; - if (diff < MIN_TRIGGER_US) { // debounce - return; - } - s_last_time = now; - struct eppp_handle *h = arg; - BaseType_t yield = false; - - // Positive edge means SPI slave prepared the data - if (gpio_get_level(h->gpio_intr) == 1) { - xSemaphoreGiveFromISR(h->ready_semaphore, &yield); - if (yield) { - portYIELD_FROM_ISR(); - } - return; - } - - // Negative edge (when master blocked) means that slave wants to transmit - if (h->blocked == MASTER_BLOCKED) { - struct packet buf = { .data = NULL, .len = -1 }; - xQueueSendFromISR(h->out_queue, &buf, &yield); - if (yield) { - portYIELD_FROM_ISR(); - } - } -} - -static esp_err_t deinit_master(esp_netif_t *netif) -{ - struct eppp_handle *h = esp_netif_get_io_driver(netif); - ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI bus"); - ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus"); - return ESP_OK; -} - -static esp_err_t init_master(struct eppp_config_spi_s *config, esp_netif_t *netif) -{ - struct eppp_handle *h = esp_netif_get_io_driver(netif); - h->spi_host = config->host; - h->gpio_intr = config->intr; - spi_bus_config_t bus_cfg = {}; - bus_cfg.mosi_io_num = config->mosi; - bus_cfg.miso_io_num = config->miso; - bus_cfg.sclk_io_num = config->sclk; - bus_cfg.quadwp_io_num = -1; - bus_cfg.quadhd_io_num = -1; - bus_cfg.max_transfer_sz = TRANSFER_SIZE; - bus_cfg.flags = 0; - bus_cfg.intr_flags = 0; - - // TODO: Init and deinit SPI bus separately (per Kconfig?) - if (spi_bus_initialize(config->host, &bus_cfg, SPI_DMA_CH_AUTO) != ESP_OK) { - return ESP_FAIL; - } - - spi_device_interface_config_t dev_cfg = {}; - dev_cfg.clock_speed_hz = config->freq; - dev_cfg.mode = 0; - dev_cfg.spics_io_num = config->cs; - dev_cfg.cs_ena_pretrans = config->cs_ena_pretrans; - dev_cfg.cs_ena_posttrans = config->cs_ena_posttrans; - dev_cfg.duty_cycle_pos = 128; - dev_cfg.input_delay_ns = config->input_delay_ns; - dev_cfg.pre_cb = NULL; - dev_cfg.post_cb = NULL; - dev_cfg.queue_size = 3; - - if (spi_bus_add_device(config->host, &dev_cfg, &h->spi_device) != ESP_OK) { - return ESP_FAIL; - } - - //GPIO config for the handshake line. - gpio_config_t io_conf = { - .intr_type = GPIO_INTR_ANYEDGE, - .mode = GPIO_MODE_INPUT, - .pull_up_en = 1, - .pin_bit_mask = BIT64(config->intr), - }; - - gpio_config(&io_conf); - gpio_install_isr_service(0); - gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE); - gpio_isr_handler_add(config->intr, gpio_isr_handler, esp_netif_get_io_driver(netif)); - return ESP_OK; -} - -static void post_setup(spi_slave_transaction_t *trans) -{ - struct eppp_handle *h = trans->user; - h->slave_last_edge = esp_timer_get_time(); - gpio_set_level(h->gpio_intr, 1); - if (h->transaction_size == 0) { // If no transaction planned: - if (h->outbound.len == 0) { // we're blocked if we don't have any data - h->blocked = SLAVE_BLOCKED; - } else { - h->blocked = SLAVE_WANTS_WRITE; // we notify the master that we want to write - esp_timer_start_once(h->timer, MIN_TRIGGER_US); - } - } -} - -static void post_trans(spi_slave_transaction_t *trans) -{ - struct eppp_handle *h = trans->user; - h->blocked = NONE; - gpio_set_level(h->gpio_intr, 0); -} - -static esp_err_t deinit_slave(esp_netif_t *netif) -{ - struct eppp_handle *h = esp_netif_get_io_driver(netif); - ESP_RETURN_ON_ERROR(spi_slave_free(h->spi_host), TAG, "Failed to free SPI slave host"); - ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI device"); - ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus"); - return ESP_OK; -} - -static esp_err_t init_slave(struct eppp_config_spi_s *config, esp_netif_t *netif) -{ - struct eppp_handle *h = esp_netif_get_io_driver(netif); - h->spi_host = config->host; - h->gpio_intr = config->intr; - spi_bus_config_t bus_cfg = {}; - bus_cfg.mosi_io_num = config->mosi; - bus_cfg.miso_io_num = config->miso; - bus_cfg.sclk_io_num = config->sclk; - bus_cfg.quadwp_io_num = -1; - bus_cfg.quadhd_io_num = -1; - bus_cfg.flags = 0; - bus_cfg.intr_flags = 0; - - //Configuration for the SPI slave interface - spi_slave_interface_config_t slvcfg = { - .mode = 0, - .spics_io_num = config->cs, - .queue_size = 3, - .flags = 0, - .post_setup_cb = post_setup, - .post_trans_cb = post_trans, - }; - - //Configuration for the handshake line - gpio_config_t io_conf = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = BIT64(config->intr), - }; - - gpio_config(&io_conf); - gpio_set_pull_mode(config->mosi, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(config->sclk, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(config->cs, GPIO_PULLUP_ONLY); - - //Initialize SPI slave interface - if (spi_slave_initialize(config->host, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO) != ESP_OK) { - return ESP_FAIL; - } - return ESP_OK; -} - -typedef esp_err_t (*perform_transaction_t)(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer); - -static esp_err_t perform_transaction_master(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer) -{ - spi_transaction_t t = {}; - t.length = len * 8; - t.tx_buffer = tx_buffer; - t.rx_buffer = rx_buffer; - return spi_device_transmit(h->spi_device, &t); -} - -static esp_err_t perform_transaction_slave(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer) -{ - spi_slave_transaction_t t = {}; - t.user = h; - t.length = len * 8; - t.tx_buffer = tx_buffer; - t.rx_buffer = rx_buffer; - return spi_slave_transmit(h->spi_host, &t, portMAX_DELAY); -} - -esp_err_t eppp_perform(esp_netif_t *netif) -{ - static WORD_ALIGNED_ATTR uint8_t out_buf[TRANSFER_SIZE] = {}; - static WORD_ALIGNED_ATTR uint8_t in_buf[TRANSFER_SIZE] = {}; - - struct eppp_handle *h = esp_netif_get_io_driver(netif); - - // Perform transaction for master and slave - const perform_transaction_t perform_transaction = h->role == EPPP_CLIENT ? perform_transaction_master : perform_transaction_slave; - - if (h->stop) { - return ESP_ERR_TIMEOUT; - } - - BaseType_t tx_queue_stat; - bool allow_test_tx = false; - uint16_t next_tx_size = 0; - if (h->role == EPPP_CLIENT) { - // SPI MASTER only code - if (xSemaphoreTake(h->ready_semaphore, pdMS_TO_TICKS(1000)) != pdTRUE) { - // slave might not be ready, but maybe we just missed an interrupt - allow_test_tx = true; - } - if (h->outbound.len == 0 && h->transaction_size == 0 && h->blocked == NONE) { - h->blocked = MASTER_BLOCKED; - xQueueReceive(h->out_queue, &h->outbound, portMAX_DELAY); - h->blocked = NONE; - if (h->outbound.len == -1) { - h->outbound.len = 0; - h->blocked = MASTER_WANTS_READ; - } - } else if (h->blocked == MASTER_WANTS_READ) { - h->blocked = NONE; - } - } - struct header *head = (void *)out_buf; - if (h->outbound.len <= h->transaction_size && allow_test_tx == false) { - // sending outbound - head->size = h->outbound.len; - if (h->outbound.len > 0) { - memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len); - free(h->outbound.data); - ESP_LOG_BUFFER_HEXDUMP(TAG, out_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE); - h->outbound.data = NULL; - h->outbound.len = 0; - } - do { - tx_queue_stat = xQueueReceive(h->out_queue, &h->outbound, 0); - } while (tx_queue_stat == pdTRUE && h->outbound.len == -1); - if (h->outbound.len == -1) { // used as a signal only, no actual data - h->outbound.len = 0; - } - } else { - // outbound is bigger, need to transmit in another transaction (keep this empty) - head->size = 0; - } - next_tx_size = head->next_size = h->outbound.len; - head->magic = SPI_HEADER_MAGIC; - head->check = esp_rom_crc16_le(0, out_buf, sizeof(struct header) - sizeof(uint16_t)); - esp_err_t ret = perform_transaction(h, sizeof(struct header) + h->transaction_size, out_buf, in_buf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "spi_device_transmit failed"); - h->transaction_size = 0; // need to start with HEADER only transaction - return ESP_FAIL; - } - head = (void *)in_buf; - uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t)); - if (check != head->check || head->magic != SPI_HEADER_MAGIC) { - h->transaction_size = 0; // need to start with HEADER only transaction - if (allow_test_tx) { - return ESP_OK; - } - ESP_LOGE(TAG, "Wrong checksum or magic"); - return ESP_FAIL; - } - if (head->size > 0) { - ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE); - esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL); - } - h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size); - return ESP_OK; -} - -#elif CONFIG_EPPP_LINK_DEVICE_UART -#define BUF_SIZE (1024) - -static esp_err_t init_uart(struct eppp_handle *h, eppp_config_t *config) -{ - h->uart_port = config->uart.port; - uart_config_t uart_config = {}; - uart_config.baud_rate = config->uart.baud; - uart_config.data_bits = UART_DATA_8_BITS; - uart_config.parity = UART_PARITY_DISABLE; - uart_config.stop_bits = UART_STOP_BITS_1; - uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; - uart_config.source_clk = UART_SCLK_DEFAULT; - - ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->uart.rx_buffer_size, 0, config->uart.queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART"); - ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params"); - ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->uart.tx_io, config->uart.rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins"); - ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout"); - return ESP_OK; -} - -static void deinit_uart(struct eppp_handle *h) -{ - uart_driver_delete(h->uart_port); -} - -esp_err_t eppp_perform(esp_netif_t *netif) -{ - static uint8_t buffer[BUF_SIZE] = {}; - struct eppp_handle *h = esp_netif_get_io_driver(netif); - uart_event_t event = {}; - if (h->stop) { - return ESP_ERR_TIMEOUT; - } - - if (xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(100)) != pdTRUE) { - return ESP_OK; - } - if (event.type == UART_DATA) { - size_t len; - uart_get_buffered_data_len(h->uart_port, &len); - if (len) { - len = uart_read_bytes(h->uart_port, buffer, BUF_SIZE, 0); - ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_VERBOSE); - esp_netif_receive(netif, buffer, len, NULL); - } - } else { - ESP_LOGW(TAG, "Received UART event: %d", event.type); - } - 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) { @@ -721,7 +218,7 @@ static void remove_handlers(void) { esp_netif_t *netif = esp_netif_find_if(have_some_eppp_netif, NULL); if (netif == NULL) { - // if EPPP netif in the system, we cleanup the statics + // if EPPP netif in the system, we clean up the statics vEventGroupDelete(s_event_group); s_event_group = NULL; esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event); @@ -734,76 +231,33 @@ void eppp_deinit(esp_netif_t *netif) if (netif == NULL) { return; } -#if CONFIG_EPPP_LINK_DEVICE_SPI struct eppp_handle *h = esp_netif_get_io_driver(netif); - if (h->role == EPPP_CLIENT) { - deinit_master(netif); - } else { - deinit_slave(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_DEVICE_ETH - eppp_transport_deinit(); -#endif - netif_deinit(netif); + EPPP_TRANSPORT_DEINIT(h); + eppp_netif_deinit(netif); } esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config) { + __attribute__((unused)) esp_err_t ret = ESP_OK; + esp_netif_t *netif = NULL; if (config == NULL || (role != EPPP_SERVER && role != EPPP_CLIENT)) { ESP_LOGE(TAG, "Invalid configuration or role"); return NULL; } - esp_netif_t *netif = netif_init(role, config); - if (!netif) { - ESP_LOGE(TAG, "Failed to initialize PPP netif"); - return NULL; - } - esp_netif_ppp_config_t netif_params; - ESP_ERROR_CHECK(esp_netif_ppp_get_params(netif, &netif_params)); - netif_params.ppp_our_ip4_addr = config->ppp.our_ip4_addr; - netif_params.ppp_their_ip4_addr = config->ppp.their_ip4_addr; - netif_params.ppp_error_event_enabled = true; - ESP_ERROR_CHECK(esp_netif_ppp_set_params(netif, &netif_params)); -#if CONFIG_EPPP_LINK_DEVICE_SPI - if (role == EPPP_CLIENT) { - init_master(&config->spi, netif); - } else { - init_slave(&config->spi, netif); - - } -#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_DEVICE_ETH - esp_err_t ret = eppp_transport_init(config, netif); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret); - return NULL; - } - -#endif + eppp_transport_handle_t h = EPPP_TRANSPORT_INIT(config); + ESP_GOTO_ON_FALSE(h, ESP_ERR_NO_MEM, err, TAG, "Failed to init EPPP transport"); + ESP_GOTO_ON_FALSE(netif = eppp_netif_init(role, h, config), ESP_ERR_NO_MEM, err, TAG, "Failed to init EPPP netif"); + ESP_GOTO_ON_ERROR(esp_netif_attach(netif, h), err, TAG, "Failed to attach netif to EPPP transport"); return netif; +err: + if (h) { + EPPP_TRANSPORT_DEINIT(h); + } + if (netif) { + eppp_netif_deinit(netif); + } + return NULL; } esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms) diff --git a/components/eppp_link/eppp_netif_tun.c b/components/eppp_link/eppp_netif_tun.c new file mode 100644 index 000000000..32d656bc0 --- /dev/null +++ b/components/eppp_link/eppp_netif_tun.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2025 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 "esp_netif_net_stack.h" +#include "lwip/esp_netif_net_stack.h" +#include "lwip/prot/ethernet.h" +#include "lwip/inet.h" +#include "lwip/sockets.h" +#include "ping/ping_sock.h" +#include "esp_check.h" +#include "esp_idf_version.h" + +static const char *TAG = "eppp_tun_netif"; + +static void tun_input(void *h, void *buffer, unsigned int len, void *eb) +{ + __attribute__((unused)) esp_err_t ret = ESP_OK; + struct netif *netif = h; + struct pbuf *p = NULL; + + ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE); + // need to alloc extra space for the ETH header to support possible packet forwarding + ESP_GOTO_ON_FALSE(p = pbuf_alloc(PBUF_RAW, len + SIZEOF_ETH_HDR, PBUF_RAM), ESP_ERR_NO_MEM, err, TAG, "pbuf_alloc failed"); + ESP_GOTO_ON_FALSE(pbuf_remove_header(p, SIZEOF_ETH_HDR) == 0, ESP_FAIL, err, TAG, "pbuf_remove_header failed"); + memcpy(p->payload, buffer, len); + ESP_GOTO_ON_FALSE(netif->input(p, netif) == ERR_OK, ESP_FAIL, err, TAG, "failed to input packet to lwip"); + return; +err: + if (p) { + pbuf_free(p); + } +} + +static err_t tun_output(struct netif *netif, struct pbuf *p) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + esp_err_t ret = esp_netif_transmit(netif->state, p->payload, p->len); + switch (ret) { + case ESP_OK: + return ERR_OK; + case ESP_ERR_NO_MEM: + return ERR_MEM; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + case ESP_ERR_ESP_NETIF_TX_FAILED: + return ERR_BUF; +#endif + case ESP_ERR_INVALID_ARG: + return ERR_ARG; + default: + return ERR_IF; + } +} + +static err_t tun_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return tun_output(netif, p); +} +static err_t tun_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return tun_output(netif, p); +} + +static err_t tun_init(struct netif *netif) +{ + if (netif == NULL) { + return ERR_IF; + } + netif->name[0] = 't'; + netif->name[1] = 'u'; +#if LWIP_IPV4 + netif->output = tun_output_v4; +#endif +#if LWIP_IPV6 + netif->output_ip6 = tun_output_v6; +#endif + netif->mtu = 1500; + return ERR_OK; +} + +static const struct esp_netif_netstack_config s_config_tun = { + .lwip = { + .init_fn = tun_init, + .input_fn = tun_input, + } +}; + +const esp_netif_netstack_config_t *g_eppp_netif_config_tun = &s_config_tun; + +static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + ESP_LOGD(TAG, "%" PRIu32 " bytes from %s icmp_seq=%" PRIu16 " ttl=%" PRIu16 " time=%" PRIu32 " ms\n", + recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time); + if (esp_ping_stop(hdl) != ESP_OK) { + ESP_LOGE(TAG, "Failed to stop ping session"); + // continue to delete the session + } + esp_ping_delete_session(hdl); + ESP_LOGI(TAG, "PING success -> stop and post IP"); + esp_netif_t *netif = (esp_netif_t *)args; + esp_netif_ip_info_t ip = {0}; + esp_netif_get_ip_info(netif, &ip); + esp_netif_set_ip_info(netif, &ip); +} + +static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + ESP_LOGD(TAG, "From %s icmp_seq=%" PRIu16 "timeout", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno); +} + +static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + ip_addr_t target_addr; + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + uint32_t loss; + + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + + if (transmitted > 0) { + loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); + } else { + loss = 0; + } + if (IP_IS_V4(&target_addr)) { + ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr))); + } else { + ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr))); + } + ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n", + transmitted, received, loss, total_time_ms); + esp_ping_delete_session(hdl); +} + +esp_err_t eppp_check_connection(esp_netif_t *netif) +{ + esp_err_t ret = ESP_OK; + esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); + config.count = 100; + ESP_LOGI(TAG, "Checking connection on EPPP interface #%" PRIu32, config.interface); + ip_addr_t target_addr = {0}; + esp_netif_ip_info_t ip; + esp_netif_get_ip_info(netif, &ip); + target_addr.u_addr.ip4.addr = ip.gw.addr; + config.target_addr = target_addr; + esp_ping_callbacks_t cbs = { + .cb_args = netif, + .on_ping_success = cmd_ping_on_ping_success, + .on_ping_timeout = cmd_ping_on_ping_timeout, + .on_ping_end = cmd_ping_on_ping_end + }; + esp_ping_handle_t ping; + ESP_GOTO_ON_ERROR(esp_ping_new_session(&config, &cbs, &ping), err, TAG, "Failed to create ping session"); + ESP_GOTO_ON_ERROR(esp_ping_start(ping), err, TAG, "Failed to start ping session"); + ESP_LOGI(TAG, "Ping started"); + return ret; +err: + ESP_LOGE(TAG, "Failed to start connection check"); + return ret; +} diff --git a/components/eppp_link/eppp_sdio.c b/components/eppp_link/eppp_sdio.c new file mode 100644 index 000000000..84a4363c0 --- /dev/null +++ b/components/eppp_link/eppp_sdio.c @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_check.h" +#include "esp_mac.h" +#include "eppp_link.h" +#include "eppp_transport.h" +#include "eppp_transport_sdio.h" + +#define TAG "eppp_sdio" + +struct eppp_sdio { + struct eppp_handle parent; + bool is_host; +}; + +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(struct eppp_config_sdio_s *config); +void eppp_sdio_slave_deinit(void); +void eppp_sdio_host_deinit(void); + +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; + } + struct eppp_sdio *handle = __containerof(h, struct eppp_sdio, parent);; + if (handle->is_host) { + return eppp_sdio_host_rx(netif); + } else { + return eppp_sdio_slave_rx(netif); + } +} + +static esp_err_t post_attach(esp_netif_t *esp_netif, void *args) +{ + eppp_transport_handle_t h = (eppp_transport_handle_t)args; + ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null"); + struct eppp_sdio *sdio = __containerof(h, struct eppp_sdio, parent); + h->base.netif = esp_netif; + + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = h, + .transmit = sdio->is_host ? eppp_sdio_host_tx : eppp_sdio_slave_tx, + }; + + ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config"); + ESP_LOGI(TAG, "EPPP SDIO transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif)); + return ESP_OK; +} + +eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config) +{ + __attribute__((unused)) esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null"); + struct eppp_sdio *h = calloc(1, sizeof(struct eppp_sdio)); + ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle"); + h->parent.base.post_attach = post_attach; + h->is_host = config->is_host; + esp_err_t (*init_fn)(struct eppp_config_sdio_s * eppp_config) = h->is_host ? eppp_sdio_host_init : eppp_sdio_slave_init; + ESP_GOTO_ON_ERROR(init_fn(config), err, TAG, "Failed to init SDIO"); + return &h->parent; +err: + free(h); + return NULL; +} + +void eppp_sdio_deinit(eppp_transport_handle_t h) +{ + struct eppp_sdio *sdio = __containerof(h, struct eppp_sdio, parent); + if (sdio->is_host) { + eppp_sdio_host_deinit(); + } else { + eppp_sdio_slave_deinit(); + } +} diff --git a/components/eppp_link/eppp_sdio_slave.c b/components/eppp_link/eppp_sdio_slave.c index ef2579570..bc971d16d 100644 --- a/components/eppp_link/eppp_sdio_slave.c +++ b/components/eppp_link/eppp_sdio_slave.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,7 @@ #include "esp_log.h" #include "esp_netif.h" #include "driver/sdio_slave.h" +#include "eppp_link.h" #include "eppp_sdio.h" #include "esp_check.h" @@ -109,7 +110,7 @@ static void event_cb(uint8_t pos) } } -esp_err_t eppp_sdio_slave_init(void) +esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *eppp_config) { sdio_slave_config_t config = { .sending_mode = SDIO_SLAVE_SEND_PACKET, @@ -166,7 +167,7 @@ void eppp_sdio_slave_deinit(void) { } -esp_err_t eppp_sdio_slave_init(void) +esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *config) { return ESP_ERR_NOT_SUPPORTED; } diff --git a/components/eppp_link/eppp_spi.c b/components/eppp_link/eppp_spi.c new file mode 100644 index 000000000..2588b5a69 --- /dev/null +++ b/components/eppp_link/eppp_spi.c @@ -0,0 +1,470 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_check.h" +#include "esp_event.h" +#include "esp_mac.h" +#include "eppp_link.h" +#include "eppp_transport.h" +#include "eppp_transport_spi.h" +#include "driver/spi_master.h" +#include "driver/spi_slave.h" +#include "driver/gpio.h" +#include "esp_timer.h" +#include "esp_rom_crc.h" + +#define TAG "eppp_spi" + +#define MAX_PAYLOAD 1500 +#define MIN_TRIGGER_US 20 +#define SPI_HEADER_MAGIC 0x1234 +#define SPI_ALIGN(size) (((size) + 3U) & ~(3U)) +#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6)) +#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */ + +struct packet { + size_t len; + uint8_t *data; +}; + +struct header { + uint16_t magic; + uint16_t size; + uint16_t next_size; + uint16_t check; +} __attribute__((packed)); + +enum blocked_status { + NONE, + MASTER_BLOCKED, + MASTER_WANTS_READ, + SLAVE_BLOCKED, + SLAVE_WANTS_WRITE, +}; + +struct eppp_spi { + struct eppp_handle parent; + bool is_master; + QueueHandle_t out_queue; + QueueHandle_t ready_semaphore; + spi_device_handle_t spi_device; + spi_host_device_t spi_host; + int gpio_intr; + uint16_t next_size; + uint16_t transaction_size; + struct packet outbound; + enum blocked_status blocked; + uint32_t slave_last_edge; + esp_timer_handle_t timer; +}; + +static esp_err_t transmit(void *h, void *buffer, size_t len) +{ + struct eppp_handle *common = h; + struct eppp_spi *handle = __containerof(common, struct eppp_spi, parent);; +#if CONFIG_EPPP_LINK_DEVICE_SPI + struct packet buf = { }; + uint8_t *current_buffer = buffer; + size_t remaining = len; + do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform + // fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD) + size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining; + buf.data = malloc(batch); + if (buf.data == NULL) { + ESP_LOGE(TAG, "Failed to allocate packet"); + return ESP_ERR_NO_MEM; + } + buf.len = batch; + remaining -= batch; + memcpy(buf.data, current_buffer, batch); + current_buffer += batch; + BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0); + if (ret != pdTRUE) { + ESP_LOGE(TAG, "Failed to queue packet to slave!"); + return ESP_ERR_NO_MEM; + } + } while (remaining > 0); + + if (!handle->is_master && handle->blocked == SLAVE_BLOCKED) { + uint32_t now = esp_timer_get_time(); + uint32_t diff = now - handle->slave_last_edge; + if (diff < MIN_TRIGGER_US) { + esp_rom_delay_us(MIN_TRIGGER_US - diff); + } + gpio_set_level(handle->gpio_intr, 0); + } + +#elif CONFIG_EPPP_LINK_DEVICE_UART + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_WARN); + uart_write_bytes(handle->uart_port, buffer, len); +#endif // DEVICE UART or SPI + return ESP_OK; +} + +static void IRAM_ATTR timer_callback(void *arg) +{ + struct eppp_spi *h = arg; + if (h->blocked == SLAVE_WANTS_WRITE) { + gpio_set_level(h->gpio_intr, 0); + } +} + +static void IRAM_ATTR gpio_isr_handler(void *arg) +{ + static uint32_t s_last_time; + uint32_t now = esp_timer_get_time(); + uint32_t diff = now - s_last_time; + if (diff < MIN_TRIGGER_US) { // debounce + return; + } + s_last_time = now; + struct eppp_spi *h = arg; + BaseType_t yield = false; + + // Positive edge means SPI slave prepared the data + if (gpio_get_level(h->gpio_intr) == 1) { + xSemaphoreGiveFromISR(h->ready_semaphore, &yield); + if (yield) { + portYIELD_FROM_ISR(); + } + return; + } + + // Negative edge (when master blocked) means that slave wants to transmit + if (h->blocked == MASTER_BLOCKED) { + struct packet buf = { .data = NULL, .len = -1 }; + xQueueSendFromISR(h->out_queue, &buf, &yield); + if (yield) { + portYIELD_FROM_ISR(); + } + } +} + +static esp_err_t deinit_master(struct eppp_spi *h) +{ + ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI bus"); + ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus"); + return ESP_OK; +} + +static esp_err_t init_master(struct eppp_config_spi_s *config, struct eppp_spi *h) +{ + esp_err_t ret = ESP_OK; + h->spi_host = config->host; + h->gpio_intr = config->intr; + spi_bus_config_t bus_cfg = {}; + bus_cfg.mosi_io_num = config->mosi; + bus_cfg.miso_io_num = config->miso; + bus_cfg.sclk_io_num = config->sclk; + bus_cfg.quadwp_io_num = -1; + bus_cfg.quadhd_io_num = -1; + bus_cfg.max_transfer_sz = TRANSFER_SIZE; + bus_cfg.flags = 0; + bus_cfg.intr_flags = 0; + + // TODO(IDF-13351): Init and deinit SPI bus separately (per Kconfig?) + ESP_RETURN_ON_ERROR(spi_bus_initialize(config->host, &bus_cfg, SPI_DMA_CH_AUTO), TAG, "Failed to init SPI bus"); + + spi_device_interface_config_t dev_cfg = {}; + dev_cfg.clock_speed_hz = config->freq; + dev_cfg.mode = 0; + dev_cfg.spics_io_num = config->cs; + dev_cfg.cs_ena_pretrans = config->cs_ena_pretrans; + dev_cfg.cs_ena_posttrans = config->cs_ena_posttrans; + dev_cfg.duty_cycle_pos = 128; + dev_cfg.input_delay_ns = config->input_delay_ns; + dev_cfg.pre_cb = NULL; + dev_cfg.post_cb = NULL; + dev_cfg.queue_size = 3; + + ESP_GOTO_ON_ERROR(spi_bus_add_device(config->host, &dev_cfg, &h->spi_device), err, TAG, "Failed to add SPI device"); + + //GPIO config for the handshake line. + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_ANYEDGE, + .mode = GPIO_MODE_INPUT, + .pull_up_en = 1, + .pin_bit_mask = BIT64(config->intr), + }; + + ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err_dev, TAG, "Failed to config interrupt GPIO"); + ESP_GOTO_ON_ERROR(gpio_install_isr_service(0), err_dev, TAG, "Failed to install GPIO ISR"); + ESP_GOTO_ON_ERROR(gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE), err_dev, TAG, "Failed to set ISR type"); + ESP_GOTO_ON_ERROR(gpio_isr_handler_add(config->intr, gpio_isr_handler, h), err_dev, TAG, "Failed to add ISR handler"); + return ESP_OK; +err_dev: + spi_bus_remove_device(h->spi_device); +err: + spi_bus_free(config->host); + return ret; +} + +static void post_setup(spi_slave_transaction_t *trans) +{ + struct eppp_spi *h = trans->user; + h->slave_last_edge = esp_timer_get_time(); + gpio_set_level(h->gpio_intr, 1); + if (h->transaction_size == 0) { // If no transaction planned: + if (h->outbound.len == 0) { // we're blocked if we don't have any data + h->blocked = SLAVE_BLOCKED; + } else { + h->blocked = SLAVE_WANTS_WRITE; // we notify the master that we want to write + esp_timer_start_once(h->timer, MIN_TRIGGER_US); + } + } +} + +static void post_transaction(spi_slave_transaction_t *transaction) +{ + struct eppp_spi *h = transaction->user; + h->blocked = NONE; + gpio_set_level(h->gpio_intr, 0); +} + +static esp_err_t deinit_slave(struct eppp_spi *h) +{ + ESP_RETURN_ON_ERROR(spi_slave_free(h->spi_host), TAG, "Failed to free SPI slave host"); + ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI device"); + ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus"); + return ESP_OK; +} + +static esp_err_t init_slave(struct eppp_config_spi_s *config, struct eppp_spi *h) +{ + h->spi_host = config->host; + h->gpio_intr = config->intr; + spi_bus_config_t bus_cfg = {}; + bus_cfg.mosi_io_num = config->mosi; + bus_cfg.miso_io_num = config->miso; + bus_cfg.sclk_io_num = config->sclk; + bus_cfg.quadwp_io_num = -1; + bus_cfg.quadhd_io_num = -1; + bus_cfg.flags = 0; + bus_cfg.intr_flags = 0; + + //Configuration for the SPI slave interface + spi_slave_interface_config_t slvcfg = { + .mode = 0, + .spics_io_num = config->cs, + .queue_size = 3, + .flags = 0, + .post_setup_cb = post_setup, + .post_trans_cb = post_transaction, + }; + + //Configuration for the handshake line + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = BIT64(config->intr), + }; + + gpio_config(&io_conf); + gpio_set_pull_mode(config->mosi, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(config->sclk, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(config->cs, GPIO_PULLUP_ONLY); + + //Initialize SPI slave interface + if (spi_slave_initialize(config->host, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO) != ESP_OK) { + return ESP_FAIL; + } + return ESP_OK; +} + +typedef esp_err_t (*perform_transaction_t)(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer); + +static esp_err_t perform_transaction_master(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer) +{ + spi_transaction_t t = {}; + t.length = len * 8; + t.tx_buffer = tx_buffer; + t.rx_buffer = rx_buffer; + return spi_device_transmit(h->spi_device, &t); +} + +static esp_err_t perform_transaction_slave(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer) +{ + spi_slave_transaction_t t = {}; + t.user = h; + t.length = len * 8; + t.tx_buffer = tx_buffer; + t.rx_buffer = rx_buffer; + return spi_slave_transmit(h->spi_host, &t, portMAX_DELAY); +} + +esp_err_t eppp_perform(esp_netif_t *netif) +{ + static WORD_ALIGNED_ATTR uint8_t out_buf[TRANSFER_SIZE] = {}; + static WORD_ALIGNED_ATTR uint8_t in_buf[TRANSFER_SIZE] = {}; + + struct eppp_handle *handle = esp_netif_get_io_driver(netif); + struct eppp_spi *h = __containerof(handle, struct eppp_spi, parent); + + // Perform transaction for master and slave + const perform_transaction_t perform_transaction = h->is_master ? perform_transaction_master : perform_transaction_slave; + + if (h->parent.stop) { + return ESP_ERR_TIMEOUT; + } + + BaseType_t tx_queue_stat; + bool allow_test_tx = false; + uint16_t next_tx_size = 0; + if (h->is_master) { + // SPI MASTER only code + if (xSemaphoreTake(h->ready_semaphore, pdMS_TO_TICKS(1000)) != pdTRUE) { + // slave might not be ready, but maybe we just missed an interrupt + allow_test_tx = true; + } + if (h->outbound.len == 0 && h->transaction_size == 0 && h->blocked == NONE) { + h->blocked = MASTER_BLOCKED; + xQueueReceive(h->out_queue, &h->outbound, portMAX_DELAY); + h->blocked = NONE; + if (h->outbound.len == -1) { + h->outbound.len = 0; + h->blocked = MASTER_WANTS_READ; + } + } else if (h->blocked == MASTER_WANTS_READ) { + h->blocked = NONE; + } + } + struct header *head = (void *)out_buf; + if (h->outbound.len <= h->transaction_size && allow_test_tx == false) { + // sending outbound + head->size = h->outbound.len; + if (h->outbound.len > 0) { + memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len); + free(h->outbound.data); + ESP_LOG_BUFFER_HEXDUMP(TAG, out_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE); + h->outbound.data = NULL; + h->outbound.len = 0; + } + do { + tx_queue_stat = xQueueReceive(h->out_queue, &h->outbound, 0); + } while (tx_queue_stat == pdTRUE && h->outbound.len == -1); + if (h->outbound.len == -1) { // used as a signal only, no actual data + h->outbound.len = 0; + } + } else { + // outbound is bigger, need to transmit in another transaction (keep this empty) + head->size = 0; + } + next_tx_size = head->next_size = h->outbound.len; + head->magic = SPI_HEADER_MAGIC; + head->check = esp_rom_crc16_le(0, out_buf, sizeof(struct header) - sizeof(uint16_t)); + esp_err_t ret = perform_transaction(h, sizeof(struct header) + h->transaction_size, out_buf, in_buf); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "spi_device_transmit failed"); + h->transaction_size = 0; // need to start with HEADER only transaction + return ESP_FAIL; + } + head = (void *)in_buf; + uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t)); + if (check != head->check || head->magic != SPI_HEADER_MAGIC) { + h->transaction_size = 0; // need to start with HEADER only transaction + if (allow_test_tx) { + return ESP_OK; + } + ESP_LOGE(TAG, "Wrong checksum or magic"); + return ESP_FAIL; + } + if (head->size > 0) { + ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE); + esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL); + } + h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size); + return ESP_OK; +} + + +static esp_err_t init_driver(struct eppp_spi *h, struct eppp_config_spi_s *config) +{ + if (config->is_master) { + return init_master(config, h); + } + return init_slave(config, h); +} + +static esp_err_t post_attach(esp_netif_t *esp_netif, void *args) +{ + eppp_transport_handle_t h = (eppp_transport_handle_t)args; + ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null"); + h->base.netif = esp_netif; + + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = h, + .transmit = transmit, + }; + + ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config"); + ESP_LOGI(TAG, "EPPP SPI transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif)); + return ESP_OK; +} + + +eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config) +{ + __attribute__((unused)) esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null"); + struct eppp_spi *h = calloc(1, sizeof(struct eppp_spi)); + ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle"); + h->is_master = config->is_master; + h->parent.base.post_attach = post_attach; + h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet)); + ESP_GOTO_ON_FALSE(h->out_queue, ESP_FAIL, err, TAG, "Failed to create the packet queue"); + if (h->is_master) { + ESP_GOTO_ON_FALSE(h->ready_semaphore = xSemaphoreCreateBinary(), ESP_FAIL, err, TAG, "Failed to create the semaphore"); + } + h->transaction_size = 0; + h->outbound.data = NULL; + h->outbound.len = 0; + if (!h->is_master) { + esp_timer_create_args_t args = { + .callback = &timer_callback, + .arg = h, + .name = "spi_slave_tmr" + }; + ESP_GOTO_ON_ERROR(esp_timer_create(&args, &h->timer), err, TAG, "Failed to create timer"); + } + ESP_GOTO_ON_ERROR(init_driver(h, config), err, TAG, "Failed to init SPI driver"); + return &h->parent; +err: + if (h->out_queue) { + vQueueDelete(h->out_queue); + } + if (h->ready_semaphore) { + vSemaphoreDelete(h->ready_semaphore); + } + if (h->out_queue) { + vQueueDelete(h->out_queue); + } + free(h); + return NULL; +} +void eppp_spi_deinit(eppp_transport_handle_t handle) +{ + struct eppp_spi *h = __containerof(handle, struct eppp_spi, parent);; + if (h->is_master) { + deinit_master(h); + } else { + deinit_slave(h); + } + struct packet buf = { }; + while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) { + if (buf.len > 0) { + free(buf.data); + } + } + vQueueDelete(h->out_queue); + if (h->is_master) { + vSemaphoreDelete(h->ready_semaphore); + } + free(h); +} diff --git a/components/eppp_link/eppp_transport.h b/components/eppp_link/eppp_transport.h index 8dbed9f46..b911e67b6 100644 --- a/components/eppp_link/eppp_transport.h +++ b/components/eppp_link/eppp_transport.h @@ -1,14 +1,17 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "esp_netif_types.h" -esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif); +struct eppp_handle { + esp_netif_driver_base_t base; + eppp_type_t role; + bool stop; + bool exited; + bool netif_stop; +}; -esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len); - -esp_err_t eppp_transport_rx(esp_netif_t *netif); - -void eppp_transport_deinit(void); +esp_err_t eppp_check_connection(esp_netif_t *netif); diff --git a/components/eppp_link/eppp_uart.c b/components/eppp_link/eppp_uart.c new file mode 100644 index 000000000..2101e93f3 --- /dev/null +++ b/components/eppp_link/eppp_uart.c @@ -0,0 +1,252 @@ +/* + * SPDX-FileCopyrightText: 2025 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 "esp_check.h" +#include "esp_event.h" +#include "eppp_link.h" +#include "eppp_transport.h" +#include "driver/uart.h" + +#define TAG "eppp_uart" + +struct eppp_uart { + struct eppp_handle parent; + QueueHandle_t uart_event_queue; + uart_port_t uart_port; +}; + +#define MAX_PAYLOAD (1500) +#define HEADER_MAGIC (0x7E) +#define HEADER_SIZE (4) +#define MAX_PACKET_SIZE (MAX_PAYLOAD + HEADER_SIZE) +/* Maximum size of a packet sent over UART, including header and payload */ +#define UART_BUF_SIZE (MAX_PACKET_SIZE) + +struct header { + uint8_t magic; + uint8_t check; + uint16_t size; +} __attribute__((packed)); + +static esp_err_t transmit(void *h, void *buffer, size_t len) +{ + struct eppp_handle *common = h; + struct eppp_uart *handle = __containerof(common, struct eppp_uart, parent); +#ifndef CONFIG_EPPP_LINK_USES_PPP + static uint8_t out_buf[MAX_PACKET_SIZE] = {}; + struct header *head = (void *)out_buf; + head->magic = HEADER_MAGIC; + head->check = 0; + head->size = len; + head->check = (0xFF & len) ^ (len >> 8); + memcpy(out_buf + sizeof(struct header), buffer, len); + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", out_buf, len + sizeof(struct header), ESP_LOG_DEBUG); + uart_write_bytes(handle->uart_port, out_buf, len + sizeof(struct header)); +#else + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_DEBUG); + uart_write_bytes(handle->uart_port, buffer, len); +#endif + return ESP_OK; +} + +static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *config) +{ + h->uart_port = config->port; + uart_config_t uart_config = {}; + uart_config.baud_rate = config->baud; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_DEFAULT; + + ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->rx_buffer_size, 0, config->queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART"); + ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params"); + ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins"); + ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout"); + return ESP_OK; +} + +static void deinit_uart(struct eppp_uart *h) +{ + uart_driver_delete(h->uart_port); +} + +#ifndef CONFIG_EPPP_LINK_USES_PPP +/** + * @brief Process incoming UART data and extract packets + */ +static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t available_data) +{ + static uint8_t in_buf[2 * UART_BUF_SIZE] = {}; + static size_t buf_start = 0; + static size_t buf_end = 0; + struct header *head; + + // Read data directly into our buffer + size_t available_space = sizeof(in_buf) - buf_end; + size_t read_size = (available_data < available_space) ? available_data : available_space; + if (read_size > 0) { + size_t len = uart_read_bytes(uart_port, in_buf + buf_end, read_size, 0); + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", in_buf + buf_end, len, ESP_LOG_DEBUG); + + if (buf_end + len <= sizeof(in_buf)) { + buf_end += len; + } else { + ESP_LOGW(TAG, "Buffer overflow, discarding data"); + buf_start = buf_end = 0; + return; + } + } + + // Process while we have enough data for at least a header + while ((buf_end - buf_start) >= sizeof(struct header)) { + head = (void *)(in_buf + buf_start); + + if (head->magic != HEADER_MAGIC) { + goto recover; + } + + uint8_t calculated_check = (head->size & 0xFF) ^ (head->size >> 8); + if (head->check != calculated_check) { + ESP_LOGW(TAG, "Checksum mismatch: expected 0x%04x, got 0x%04x", calculated_check, head->check); + goto recover; + } + + // Check if we have the complete packet + uint16_t payload_size = head->size; + size_t total_packet_size = sizeof(struct header) + payload_size; + + if (payload_size > MAX_PAYLOAD) { + ESP_LOGW(TAG, "Invalid payload size: %d", payload_size); + goto recover; + } + + // If we don't have the complete packet yet, wait for more data + if ((buf_end - buf_start) < total_packet_size) { + ESP_LOGD(TAG, "Incomplete packet: got %d bytes, need %d bytes", (buf_end - buf_start), total_packet_size); + break; + } + + // Got a complete packet, pass it to network + esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL); + + // Advance start pointer past this packet + buf_start += total_packet_size; + + // compact if we don't have enough space for 1x UART_BUF_SIZE + if (buf_start > (sizeof(in_buf) / 2) || (sizeof(in_buf) - buf_end) < UART_BUF_SIZE) { + if (buf_start < buf_end) { + size_t remaining_data = buf_end - buf_start; + memmove(in_buf, in_buf + buf_start, remaining_data); + buf_end = remaining_data; + } else { + buf_end = 0; + } + buf_start = 0; + } + + continue; + +recover: + // Search for next HEADER_MAGIC occurrence + uint8_t *next_magic = memchr(in_buf + buf_start + 1, HEADER_MAGIC, buf_end - buf_start - 1); + if (next_magic) { + // Found next potential header, advance start to that position + buf_start = next_magic - in_buf; + + // Check if we need to compact after recovery too + if (buf_start > (sizeof(in_buf) / 2) || (sizeof(in_buf) - buf_end) < UART_BUF_SIZE) { + if (buf_start < buf_end) { + size_t remaining_data = buf_end - buf_start; + memmove(in_buf, in_buf + buf_start, remaining_data); + buf_end = remaining_data; + } else { + buf_end = 0; + } + buf_start = 0; + } + } else { + // No more HEADER_MAGIC found, discard all data + buf_start = buf_end = 0; + } + } +} +#endif + +esp_err_t eppp_perform(esp_netif_t *netif) +{ + struct eppp_handle *handle = esp_netif_get_io_driver(netif); + struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent); + + uart_event_t event = {}; + if (h->parent.stop) { + return ESP_ERR_TIMEOUT; + } + + if (xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(100)) != pdTRUE) { + return ESP_OK; + } + if (event.type == UART_DATA) { + size_t len; + uart_get_buffered_data_len(h->uart_port, &len); + if (len) { +#ifdef CONFIG_EPPP_LINK_USES_PPP + static uint8_t buffer[UART_BUF_SIZE] = {}; + len = uart_read_bytes(h->uart_port, buffer, UART_BUF_SIZE, 0); + ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_DEBUG); + esp_netif_receive(netif, buffer, len, NULL); +#else + // Read directly in process_packet to save one buffer + process_packet(netif, h->uart_port, len); +#endif + } + } else { + ESP_LOGW(TAG, "Received UART event: %d", event.type); + } + return ESP_OK; +} + + +static esp_err_t post_attach(esp_netif_t *esp_netif, void *args) +{ + eppp_transport_handle_t h = (eppp_transport_handle_t)args; + ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null"); + h->base.netif = esp_netif; + + esp_netif_driver_ifconfig_t driver_ifconfig = { + .handle = h, + .transmit = transmit, + }; + + ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config"); + ESP_LOGI(TAG, "EPPP UART transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif)); + return ESP_OK; +} + +eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config) +{ + __attribute__((unused)) esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null"); + struct eppp_uart *h = calloc(1, sizeof(struct eppp_uart)); + ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle"); + h->parent.base.post_attach = post_attach; + ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART"); + return &h->parent; +err: + return NULL; +} + +void eppp_uart_deinit(eppp_transport_handle_t handle) +{ + struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent); + deinit_uart(h); + free(h); +} diff --git a/components/eppp_link/examples/host/main/Kconfig.projbuild b/components/eppp_link/examples/host/main/Kconfig.projbuild index 5029beb56..f594a728c 100644 --- a/components/eppp_link/examples/host/main/Kconfig.projbuild +++ b/components/eppp_link/examples/host/main/Kconfig.projbuild @@ -26,10 +26,66 @@ menu "Example Configuration" help Init and run iperf console. + config EXAMPLE_SPI_HOST + int "SPI Host" + depends on EPPP_LINK_DEVICE_SPI + default 1 + range 0 2 + help + SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2). + + config EXAMPLE_SPI_MOSI_PIN + int "MOSI Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 23 + range 0 39 + help + Pin number of SPI MOSI. + + config EXAMPLE_SPI_MISO_PIN + int "MISO Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 19 + range 0 39 + help + Pin number of SPI MISO. + + config EXAMPLE_SPI_SCLK_PIN + int "SCLK Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 18 + range 0 39 + help + Pin number of SPI SCLK. + + config EXAMPLE_SPI_CS_PIN + int "CS Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 5 + range 0 39 + help + Pin number of SPI CS. + + config EXAMPLE_SPI_INTR_PIN + int "Interrupt Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 17 + range 0 39 + help + Pin number of SPI interrupt. + + config EXAMPLE_SPI_FREQUENCY + int "SPI Frequency (Hz)" + depends on EPPP_LINK_DEVICE_SPI + default 4000000 + range 100000 80000000 + help + SPI frequency in Hz. + config EXAMPLE_UART_TX_PIN int "TXD Pin Number" depends on EPPP_LINK_DEVICE_UART - default 10 + default 17 range 0 31 help Pin number of UART TX. @@ -37,7 +93,7 @@ menu "Example Configuration" config EXAMPLE_UART_RX_PIN int "RXD Pin Number" depends on EPPP_LINK_DEVICE_UART - default 11 + default 18 range 0 31 help Pin number of UART RX. @@ -45,7 +101,7 @@ menu "Example Configuration" config EXAMPLE_UART_BAUDRATE int "Baudrate" depends on EPPP_LINK_DEVICE_UART - default 2000000 + default 921600 range 0 4000000 help Baudrate used by the PPP over UART diff --git a/components/eppp_link/examples/host/main/app_main.c b/components/eppp_link/examples/host/main/app_main.c index 2651b4b1e..c147d87ee 100644 --- a/components/eppp_link/examples/host/main/app_main.c +++ b/components/eppp_link/examples/host/main/app_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -104,13 +104,24 @@ void app_main(void) eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); #if CONFIG_EPPP_LINK_DEVICE_SPI config.transport = EPPP_TRANSPORT_SPI; + config.spi.is_master = true; + config.spi.host = CONFIG_EXAMPLE_SPI_HOST; + config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN; + config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN; + config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN; + config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN; + config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN; + config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY; #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; +#elif CONFIG_EPPP_LINK_DEVICE_ETH + config.transport = EPPP_TRANSPORT_ETHERNET; #else config.transport = EPPP_TRANSPORT_SDIO; + config.sdio.is_host = true; #endif esp_netif_t *eppp_netif = eppp_connect(&config); if (eppp_netif == NULL) { diff --git a/components/eppp_link/examples/slave/main/Kconfig.projbuild b/components/eppp_link/examples/slave/main/Kconfig.projbuild index c1d14e785..e17f711b4 100644 --- a/components/eppp_link/examples/slave/main/Kconfig.projbuild +++ b/components/eppp_link/examples/slave/main/Kconfig.projbuild @@ -18,10 +18,66 @@ menu "Example Configuration" help Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. + config EXAMPLE_SPI_HOST + int "SPI Host" + depends on EPPP_LINK_DEVICE_SPI + default 1 + range 0 2 + help + SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2). + + config EXAMPLE_SPI_MOSI_PIN + int "MOSI Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 23 + range 0 39 + help + Pin number of SPI MOSI. + + config EXAMPLE_SPI_MISO_PIN + int "MISO Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 19 + range 0 39 + help + Pin number of SPI MISO. + + config EXAMPLE_SPI_SCLK_PIN + int "SCLK Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 18 + range 0 39 + help + Pin number of SPI SCLK. + + config EXAMPLE_SPI_CS_PIN + int "CS Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 5 + range 0 39 + help + Pin number of SPI CS. + + config EXAMPLE_SPI_INTR_PIN + int "Interrupt Pin Number" + depends on EPPP_LINK_DEVICE_SPI + default 17 + range 0 39 + help + Pin number of SPI interrupt. + + config EXAMPLE_SPI_FREQUENCY + int "SPI Frequency (Hz)" + depends on EPPP_LINK_DEVICE_SPI + default 1000000 + range 100000 80000000 + help + SPI frequency in Hz. + config EXAMPLE_UART_TX_PIN int "TXD Pin Number" depends on EPPP_LINK_DEVICE_UART - default 11 + default 18 range 0 31 help Pin number of UART TX. @@ -29,7 +85,7 @@ menu "Example Configuration" config EXAMPLE_UART_RX_PIN int "RXD Pin Number" depends on EPPP_LINK_DEVICE_UART - default 10 + default 17 range 0 31 help Pin number of UART RX. @@ -37,7 +93,7 @@ menu "Example Configuration" config EXAMPLE_UART_BAUDRATE int "Baudrate" depends on EPPP_LINK_DEVICE_UART - default 2000000 + default 921600 range 0 4000000 help Baudrate used by the PPP over UART diff --git a/components/eppp_link/examples/slave/main/eppp_slave.c b/components/eppp_link/examples/slave/main/eppp_slave.c index 9b782d9f4..ff6dd6e05 100644 --- a/components/eppp_link/examples/slave/main/eppp_slave.c +++ b/components/eppp_link/examples/slave/main/eppp_slave.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -55,9 +55,6 @@ void init_network_interface(void) { s_wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK(esp_netif_init()); - - ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); @@ -66,15 +63,15 @@ void init_network_interface(void) esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &event_handler, - NULL, - &instance_any_id)); + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, - IP_EVENT_STA_GOT_IP, - &event_handler, - NULL, - &instance_got_ip)); + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); wifi_config_t wifi_config = { .sta = { @@ -82,9 +79,9 @@ void init_network_interface(void) .password = CONFIG_ESP_WIFI_PASSWORD, }, }; - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_ERROR_CHECK(esp_wifi_start() ); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "wifi_init_sta finished."); @@ -127,13 +124,21 @@ void app_main(void) } ESP_ERROR_CHECK(ret); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); 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; + config.spi.is_master = false; + config.spi.host = CONFIG_EXAMPLE_SPI_HOST; + config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN; + config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN; + config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN; + config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN; + config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN; + config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY; #elif CONFIG_EPPP_LINK_DEVICE_UART config.transport = EPPP_TRANSPORT_UART; config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN; diff --git a/components/eppp_link/idf_component.yml b/components/eppp_link/idf_component.yml index bf21675db..44d0bf8d9 100644 --- a/components/eppp_link/idf_component.yml +++ b/components/eppp_link/idf_component.yml @@ -4,4 +4,3 @@ description: The component provides a general purpose PPP connectivity, typicall dependencies: idf: '>=5.2' espressif/esp_serial_slave_link: "^1.1.0" - espressif/ethernet_init: '>=0.0.7' diff --git a/components/eppp_link/include/eppp_link.h b/components/eppp_link/include/eppp_link.h index 2159cec06..ca51e23b7 100644 --- a/components/eppp_link/include/eppp_link.h +++ b/components/eppp_link/include/eppp_link.h @@ -1,14 +1,14 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#pragma once #define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1) #define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2) -#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \ - .transport = EPPP_TRANSPORT_UART, \ +#define EPPP_DEFAULT_SPI_CONFIG() \ .spi = { \ .host = 1, \ .mosi = 11, \ @@ -21,6 +21,8 @@ .cs_ena_pretrans = 0, \ .cs_ena_posttrans = 0, \ }, \ + +#define EPPP_DEFAULT_UART_CONFIG() \ .uart = { \ .port = 1, \ .baud = 921600, \ @@ -29,15 +31,54 @@ .queue_size = 16, \ .rx_buffer_size = 1024, \ }, \ + +#define EPPP_DEFAULT_SDIO_CONFIG() \ .sdio = { \ .width = 4, \ .clk = 18, \ .cmd = 19, \ - .d0 = 49, \ - .d1 = 50, \ + .d0 = 14, \ + .d1 = 15, \ .d2 = 16, \ .d3 = 17, \ }, \ + +#define EPPP_DEFAULT_ETH_CONFIG() \ + .ethernet = { \ + .mdc_io = 23, \ + .mdio_io = 18, \ + .phy_addr = 1, \ + .rst_io= 5, \ + }, \ + +#if CONFIG_EPPP_LINK_DEVICE_SPI +#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_SPI_CONFIG() +#define EPPP_TRANSPORT_INIT(cfg) eppp_spi_init(&cfg->spi) +#define EPPP_TRANSPORT_DEINIT(handle) eppp_spi_deinit(handle) + +#elif CONFIG_EPPP_LINK_DEVICE_UART +#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_UART_CONFIG() +#define EPPP_TRANSPORT_INIT(cfg) eppp_uart_init(&cfg->uart) +#define EPPP_TRANSPORT_DEINIT(handle) eppp_uart_deinit(handle) + +#elif CONFIG_EPPP_LINK_DEVICE_SDIO +#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_SDIO_CONFIG() +#define EPPP_TRANSPORT_INIT(cfg) eppp_sdio_init(&cfg->sdio) +#define EPPP_TRANSPORT_DEINIT(handle) eppp_sdio_deinit(handle) + +#elif CONFIG_EPPP_LINK_DEVICE_ETH +#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_ETH_CONFIG() +#define EPPP_TRANSPORT_INIT(cfg) eppp_eth_init(&cfg->ethernet) +#define EPPP_TRANSPORT_DEINIT(handle) eppp_eth_deinit(handle) + +#else +#error Unexpeted transport +#endif + + +#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \ + .transport = EPPP_TRANSPORT_UART, \ + EPPP_DEFAULT_TRANSPORT_CONFIG() \ . task = { \ .run_task = true, \ .stack_size = 4096, \ @@ -72,6 +113,7 @@ typedef struct eppp_config_t { struct eppp_config_spi_s { int host; + bool is_master; int mosi; int miso; int sclk; @@ -93,6 +135,7 @@ typedef struct eppp_config_t { } uart; struct eppp_config_sdio_s { + bool is_host; int width; int clk; int cmd; @@ -102,6 +145,13 @@ typedef struct eppp_config_t { int d3; } sdio; + struct eppp_config_ethernet_s { + int mdc_io; + int mdio_io; + int phy_addr; + int rst_io; + } ethernet; + struct eppp_config_task_s { bool run_task; int stack_size; @@ -117,20 +167,39 @@ typedef struct eppp_config_t { } eppp_config_t; +typedef struct eppp_handle* eppp_transport_handle_t; + esp_netif_t *eppp_connect(eppp_config_t *config); esp_netif_t *eppp_listen(eppp_config_t *config); void eppp_close(esp_netif_t *netif); +/** + * @brief Initialize the EPPP link layer + * @param role + * @param config + * @return + */ esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config); void eppp_deinit(esp_netif_t *netif); esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms); +esp_netif_t *eppp_netif_init(eppp_type_t role, eppp_transport_handle_t h, eppp_config_t *eppp_config); + esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms); esp_err_t eppp_netif_start(esp_netif_t *netif); +void eppp_netif_deinit(esp_netif_t *netif); + +/** + * @brief Performs EPPP link task operation (Used for task-less implementation) + * @param netif + * @return + * - ESP_OK on success, ESP_FAIL on failure: the operation shall continue + * - ESP_ERR_TIMEOUT indicates that the operation was requested to stop + */ esp_err_t eppp_perform(esp_netif_t *netif); diff --git a/components/eppp_link/include/eppp_transport_eth.h b/components/eppp_link/include/eppp_transport_eth.h new file mode 100644 index 000000000..92ba34a64 --- /dev/null +++ b/components/eppp_link/include/eppp_transport_eth.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "eppp_link.h" + +eppp_transport_handle_t eppp_eth_init(struct eppp_config_ethernet_s *config); +void eppp_eth_deinit(eppp_transport_handle_t h); diff --git a/components/eppp_link/include/eppp_transport_sdio.h b/components/eppp_link/include/eppp_transport_sdio.h new file mode 100644 index 000000000..c758afba6 --- /dev/null +++ b/components/eppp_link/include/eppp_transport_sdio.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "eppp_link.h" + +eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config); +void eppp_sdio_deinit(eppp_transport_handle_t h); diff --git a/components/eppp_link/include/eppp_transport_spi.h b/components/eppp_link/include/eppp_transport_spi.h new file mode 100644 index 000000000..648a185e6 --- /dev/null +++ b/components/eppp_link/include/eppp_transport_spi.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "eppp_link.h" + +eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config); +void eppp_spi_deinit(eppp_transport_handle_t h); diff --git a/components/eppp_link/include/eppp_transport_uart.h b/components/eppp_link/include/eppp_transport_uart.h new file mode 100644 index 000000000..4820bc6d3 --- /dev/null +++ b/components/eppp_link/include/eppp_transport_uart.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "eppp_link.h" + +eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config); +void eppp_uart_deinit(eppp_transport_handle_t h);