diff --git a/components/eppp_link/.cz.yaml b/components/eppp_link/.cz.yaml index 34bd2dccbe..05a1e661ca 100644 --- a/components/eppp_link/.cz.yaml +++ b/components/eppp_link/.cz.yaml @@ -3,6 +3,6 @@ commitizen: bump_message: 'bump(eppp): $current_version -> $new_version' pre_bump_hooks: python ../../ci/changelog.py eppp_link tag_format: eppp-v$version - version: 0.3.1 + version: 1.0.0 version_files: - idf_component.yml diff --git a/components/eppp_link/CHANGELOG.md b/components/eppp_link/CHANGELOG.md index 2c1d3a6886..cb24a60bca 100644 --- a/components/eppp_link/CHANGELOG.md +++ b/components/eppp_link/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [1.0.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.0) + +### Features + +- Add support for custom channels ([4ee9360f](https://github.com/espressif/esp-protocols/commit/4ee9360f)) + ## [0.3.1](https://github.com/espressif/esp-protocols/commits/eppp-v0.3.1) ### Bug Fixes diff --git a/components/eppp_link/Kconfig b/components/eppp_link/Kconfig index b8ede693ef..b0457f7849 100644 --- a/components/eppp_link/Kconfig +++ b/components/eppp_link/Kconfig @@ -93,4 +93,21 @@ menu "eppp_link" default "06:00:00:00:00:02" depends on EPPP_LINK_DEVICE_ETH + config EPPP_LINK_CHANNELS_SUPPORT + bool "Enable channel support (multiple logical channels)" + default n + depends on !EPPP_LINK_DEVICE_ETH + help + Enable support for multiple logical channels in the EPPP link layer. + When enabled, you can configure the number of channels used for communication. + + config EPPP_LINK_NR_OF_CHANNELS + int "Number of logical channels" + depends on EPPP_LINK_CHANNELS_SUPPORT && !EPPP_LINK_DEVICE_ETH + range 1 8 + default 2 + help + Set the number of logical channels for EPPP link communication. + Each channel can be used for independent data streams. + endmenu diff --git a/components/eppp_link/README.md b/components/eppp_link/README.md index 5ce78c3c6f..13e523770c 100644 --- a/components/eppp_link/README.md +++ b/components/eppp_link/README.md @@ -1,7 +1,7 @@ # ESP PPP Link component (eppp_link) 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. +This component could be used for extending network using physical serial connection. Applications could vary from providing RPC 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 @@ -21,6 +21,27 @@ brings in the WiFi connectivity from the communication coprocessor. +----------------+ +----------------+ ``` +## Features + +### Network Interface Modes + +Standard PPP Mode (where PPP protocols is preferred) or simple tunnel using TUN Mode. + +### Transport layer + +UART, SPI, SDIO, Ethernet + +### Support for logical channels + +Allows channeling custom data (e.g. 802.11 frames) + +## (Other) usecases + +Besides the communication coprocessor example mentioned above, this component could be used to: +* Bring Wi-Fi connectivity to a computer using ESP32 chip. +* Connect your microcontroller to the internet via a pppd server (running on a raspberry) +* Bridging two networks with two microcontrollers + ## Configuration ### Choose the transport layer @@ -39,6 +60,14 @@ Use `idf.py menuconfig` to select the transport layer: Use PPP netif for UART; Keep the default (TUN) for others +### Channel support (multiple logical channels) + +* `CONFIG_EPPP_LINK_CHANNELS_SUPPORT` -- Enable support for multiple logical channels (default: disabled) +* `CONFIG_EPPP_LINK_NR_OF_CHANNELS` -- Number of logical channels (default: 2, range: 1-8, only visible if channel support is enabled) + +When channel support is enabled, the EPPP link can multiplex multiple logical data streams over the same transport. The number of channels is configurable. Channel support is not available for Ethernet transport. + +To use channels in your application, use the `eppp_add_channels()` API and provide your own channel transmit/receive callbacks. These APIs and related types are only available when channel support is enabled in Kconfig. ## API @@ -57,6 +86,9 @@ Use PPP netif for UART; Keep the default (TUN) for others * `eppp_netif_start()` -- Starts the network, could be called after startup or whenever a connection is lost * `eppp_netif_stop()` -- Stops the network * `eppp_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration) +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +* `eppp_add_channels()` -- Register channel transmit/receive callbacks (only available if channel support is enabled) +#endif ## Throughput diff --git a/components/eppp_link/detailed_description.md b/components/eppp_link/detailed_description.md new file mode 100644 index 0000000000..8c48297ef5 --- /dev/null +++ b/components/eppp_link/detailed_description.md @@ -0,0 +1,370 @@ +# ESP PPP Link Component (eppp_link) - Detailed Documentation + +## Overview + +The ESP PPP Link component provides a versatile communication bridge between two ESP32 microcontrollers, enabling network connectivity over various physical transport layers. One device acts as a server (typically providing connectivity), while the other acts as a client (consuming connectivity). + +## Network Interface Modes + +### PPP Mode vs TUN Mode + +The component supports two distinct network interface modes: + +#### PPP Mode (`CONFIG_EPPP_LINK_USES_PPP=y`) +- **Standard PPP Protocol**: Uses the Point-to-Point Protocol (RFC 1661) with full LCP negotiation +- **Compatibility**: Compatible with standard PPP implementations like Linux `pppd` +- **Features**: + - Automatic IP address negotiation + - Link Control Protocol (LCP) for connection establishment + - Authentication support (if configured) + - Standard PPP framing with escape sequences +- **Use Case**: When interfacing with standard PPP-capable systems or when full PPP compatibility is required +- **Transport Limitation**: Primarily designed for UART transport due to PPP's serial nature + +#### TUN Mode (`CONFIG_EPPP_LINK_USES_PPP=n`, default) +- **Simplified Packet Interface**: Uses a custom packet-based protocol without PPP negotiation +- **Performance**: Faster data transfer due to reduced protocol overhead +- **Features**: + - Direct IP packet transmission + - Custom framing for packet boundaries + - No negotiation overhead + - Static IP address configuration +- **Use Case**: Default mode for ESP-to-ESP communication, optimal for non-UART transports +- **Transport Support**: Works efficiently with all transport types (UART, SPI, SDIO, Ethernet) + +**Mode Selection**: Configure via `idf.py menuconfig` → `Component config` → `eppp_link` → `Use PPP network interface` + +## Transport Layer Options + +### UART Transport +- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_UART=y` +- **Features**: + - Simple serial communication + - Configurable baud rate (up to 3Mbps tested) + - Hardware flow control support + - Custom framing for packet boundaries in TUN mode +- **Performance**: ~2 Mbps (TCP/UDP) @ 3 Mbaud +- **Use Case**: Basic connectivity, long-distance communication, debugging +- **Pins**: TX, RX configurable + +```c +eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); +config.transport = EPPP_TRANSPORT_UART; +config.uart.tx_io = 25; +config.uart.rx_io = 26; +config.uart.baud = 921600; +``` + +### SPI Transport +- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SPI=y` +- **Features**: + - Master/slave configuration + - GPIO interrupt signaling for flow control + - Configurable clock frequency + - Full-duplex communication + - Packet queue for transmission +- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) @ 16MHz +- **Use Case**: High-speed local communication, PCB-level connections +- **Pins**: MOSI, MISO, SCLK, CS, interrupt GPIO + +```c +eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); +config.transport = EPPP_TRANSPORT_SPI; +config.spi.is_master = true; +config.spi.freq = 16000000; +config.spi.mosi = 11; +config.spi.miso = 13; +config.spi.sclk = 12; +config.spi.cs = 10; +config.spi.intr = 2; +``` + +### SDIO Transport +- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SDIO=y` +- **Features**: + - Host/slave configuration + - High-speed data transfer + - 1-bit or 4-bit bus width + - Hardware flow control +- **Performance**: ~9 Mbps (TCP), ~11 Mbps (UDP) +- **Use Case**: Highest throughput applications, module-to-module communication +- **Pins**: CLK, CMD, D0-D3 (configurable width) + +```c +eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); +config.transport = EPPP_TRANSPORT_SDIO; +config.sdio.is_host = true; +config.sdio.width = 4; +config.sdio.clk = 18; +config.sdio.cmd = 19; +config.sdio.d0 = 14; +// ... additional data pins +``` + +### Ethernet Transport +- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_ETH=y` +- **Features**: + - Direct MAC-to-MAC communication + - Can work with or without PHY chips + - Standard Ethernet framing + - Automatic task management (no `eppp_perform()` needed) +- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) with internal EMAC +- **Use Case**: Board-to-board communication, integration with existing Ethernet infrastructure +- **Pins**: MDC, MDIO, plus PHY-specific pins + +```c +eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); +config.transport = EPPP_TRANSPORT_ETHERNET; +config.ethernet.mdc_io = 23; +config.ethernet.mdio_io = 18; +config.ethernet.phy_addr = 1; +config.ethernet.rst_io = 5; +``` + +## Channel Support (Multiple Logical Channels) + +### Overview +Channel support allows multiplexing multiple independent data streams over a single transport connection. This enables applications to separate different types of data (e.g., network traffic, control commands, sensor data) into distinct logical channels. + +### Configuration +- **Enable**: `CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y` +- **Count**: `CONFIG_EPPP_LINK_NR_OF_CHANNELS` (1-8 channels, default 2) +- **Limitation**: Not available for Ethernet transport + +### Channel Usage +```c +// Channel callback function type +typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len); + +// Register channel callbacks +esp_err_t eppp_add_channels(esp_netif_t *netif, + eppp_channel_fn_t *tx, // Transmit function pointer (output) + const eppp_channel_fn_t rx, // Receive callback (input) + void* context); // User context + +// Get user context +void* eppp_get_context(esp_netif_t *netif); +``` + +### Channel Example +```c +// Channel 0: Default network traffic (handled automatically) +// Channel 1: Control/chat messages +// Channel 2: WiFi data forwarding + +static esp_err_t channel_receive(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + switch(channel) { + case 1: // Control channel + process_control_message(buffer, len); + break; + case 2: // WiFi channel + forward_to_wifi(buffer, len); + break; + default: + ESP_LOGE(TAG, "Unknown channel %d", channel); + return ESP_FAIL; + } + return ESP_OK; +} + +// Register channels +eppp_channel_fn_t tx_func; +eppp_add_channels(netif, &tx_func, channel_receive, user_context); + +// Transmit on specific channel +tx_func(netif, 1, "Hello", 5); // Send to channel 1 +``` + +## API Reference + +### Simple API (Recommended for most use cases) + +#### Client Side +```c +esp_netif_t *eppp_connect(eppp_config_t *config); +``` +- **Purpose**: Simplified client connection +- **Behavior**: Blocks until connection is established +- **Returns**: Configured network interface or NULL on failure +- **Use Case**: Simple applications that don't need fine-grained control + +#### Server Side +```c +esp_netif_t *eppp_listen(eppp_config_t *config); +``` +- **Purpose**: Simplified server listening +- **Behavior**: Blocks until client connects +- **Returns**: Configured network interface or NULL on failure +- **Use Case**: Simple applications that don't need fine-grained control + +#### Connection Management +```c +void eppp_close(esp_netif_t *netif); +``` +- **Purpose**: Close connection and cleanup resources +- **Behavior**: Stops tasks, closes transport, destroys network interface + +### Advanced API (Manual Control) + +#### Initialization +```c +esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config); +``` +- **Purpose**: Initialize endpoint without starting communication +- **Parameters**: + - `role`: `EPPP_SERVER` or `EPPP_CLIENT` + - `config`: Transport and network configuration +- **Returns**: Network interface handle +- **Use Case**: Applications needing manual control over connection lifecycle + +#### Connection Control +```c +esp_err_t eppp_netif_start(esp_netif_t *netif); +esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms); +``` +- **Purpose**: Manual network interface start/stop +- **Use Case**: Dynamic connection management, error recovery + +#### Task Management +```c +esp_err_t eppp_perform(esp_netif_t *netif); +``` +- **Purpose**: Single iteration of communication task +- **Returns**: + - `ESP_OK`: Continue operation + - `ESP_FAIL`: Operation failed but should continue + - `ESP_ERR_TIMEOUT`: Stop operation requested +- **Use Case**: Task-less operation, integration with custom task schedulers +- **Note**: Not needed for Ethernet transport (has its own task) + +#### Resource Management +```c +void eppp_deinit(esp_netif_t *netif); +``` +- **Purpose**: Clean up resources without stopping tasks +- **Use Case**: Manual resource management + +## Configuration Examples + +### Basic Client-Server Setup + +**Client (Host) Configuration:** +```c +void app_main(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); + config.transport = EPPP_TRANSPORT_UART; + config.uart.tx_io = 25; + config.uart.rx_io = 26; + config.uart.baud = 921600; + + esp_netif_t *netif = eppp_connect(&config); + if (netif == NULL) { + ESP_LOGE(TAG, "Failed to connect"); + return; + } + + ESP_LOGI(TAG, "Connected successfully"); + // Use network interface for communication +} +``` + +**Server (Slave) Configuration:** +```c +void app_main(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Initialize WiFi or other network interface + init_wifi(); + + eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG(); + config.transport = EPPP_TRANSPORT_UART; + config.uart.tx_io = 26; // Crossed with client + config.uart.rx_io = 25; // Crossed with client + config.uart.baud = 921600; + + esp_netif_t *netif = eppp_listen(&config); + if (netif == NULL) { + ESP_LOGE(TAG, "Failed to setup server"); + return; + } + + // Enable NAT to share WiFi connection + ESP_ERROR_CHECK(esp_netif_napt_enable(netif)); + ESP_LOGI(TAG, "Server ready"); +} +``` + +### Advanced Manual Control +```c +void app_main(void) +{ + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); + config.task.run_task = false; // Disable automatic task + + esp_netif_t *netif = eppp_init(EPPP_CLIENT, &config); + if (netif == NULL) { + ESP_LOGE(TAG, "Failed to initialize"); + return; + } + + // Start network interface + ESP_ERROR_CHECK(eppp_netif_start(netif)); + + // Custom task loop + while (true) { + esp_err_t ret = eppp_perform(netif); + if (ret == ESP_ERR_TIMEOUT) { + ESP_LOGI(TAG, "Operation stopped"); + break; + } else if (ret != ESP_OK) { + ESP_LOGE(TAG, "Operation failed: %s", esp_err_to_name(ret)); + } + + // Add custom processing here + vTaskDelay(pdMS_TO_TICKS(1)); + } + + eppp_deinit(netif); +} +``` + +### Multi-Channel Configuration +```c +typedef struct { + eppp_channel_fn_t tx_func; + esp_netif_t *netif; +} channel_context_t; + +static esp_err_t channel_rx(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + ESP_LOGI(TAG, "Channel %d received %d bytes", channel, len); + // Process channel data based on channel number + return ESP_OK; +} + +void setup_channels(void) +{ + eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG(); + esp_netif_t *netif = eppp_connect(&config); + + channel_context_t *ctx = calloc(1, sizeof(channel_context_t)); + ctx->netif = netif; + + // Register channel callbacks + ESP_ERROR_CHECK(eppp_add_channels(netif, &ctx->tx_func, channel_rx, ctx)); + + // Send data on channel 1 + const char *msg = "Hello on channel 1"; + ctx->tx_func(netif, 1, (void*)msg, strlen(msg)); +} +``` diff --git a/components/eppp_link/eppp_link.c b/components/eppp_link/eppp_link.c index 8dc8fb5381..350ec9893f 100644 --- a/components/eppp_link/eppp_link.c +++ b/components/eppp_link/eppp_link.c @@ -367,3 +367,24 @@ void eppp_close(esp_netif_t *netif) eppp_deinit(netif); remove_handlers(); } + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context) +{ + ESP_RETURN_ON_FALSE(netif != NULL && tx != NULL && rx != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments"); + struct eppp_handle *h = esp_netif_get_io_driver(netif); + ESP_RETURN_ON_FALSE(h != NULL && h->channel_tx != NULL, ESP_ERR_INVALID_STATE, TAG, "Transport not initialized"); + *tx = h->channel_tx; + h->channel_rx = rx; + h->context = context; + return ESP_OK; +} + +void* eppp_get_context(esp_netif_t *netif) +{ + ESP_RETURN_ON_FALSE(netif != NULL, NULL, TAG, "Invalid netif"); + struct eppp_handle *h = esp_netif_get_io_driver(netif); + ESP_RETURN_ON_FALSE(h != NULL, NULL, TAG, "EPPP Not initialized"); + return h->context; +} +#endif diff --git a/components/eppp_link/eppp_sdio.c b/components/eppp_link/eppp_sdio.c index 84a4363c03..efe0d7be75 100644 --- a/components/eppp_link/eppp_sdio.c +++ b/components/eppp_link/eppp_sdio.c @@ -13,6 +13,7 @@ #include "eppp_link.h" #include "eppp_transport.h" #include "eppp_transport_sdio.h" +#include "eppp_sdio.h" #define TAG "eppp_sdio" @@ -67,6 +68,9 @@ eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config) 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"); +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT + h->parent.channel_tx = eppp_sdio_transmit_channel; +#endif 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; diff --git a/components/eppp_link/eppp_sdio.h b/components/eppp_link/eppp_sdio.h index b6d1e4a69a..f9698533bd 100644 --- a/components/eppp_link/eppp_sdio.h +++ b/components/eppp_link/eppp_sdio.h @@ -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 */ @@ -8,8 +8,10 @@ #define MAX_SDIO_PAYLOAD 1500 #define SDIO_ALIGN(size) (((size) + 3U) & ~(3U)) #define SDIO_PAYLOAD SDIO_ALIGN(MAX_SDIO_PAYLOAD) +#define SDIO_PACKET_SIZE SDIO_ALIGN(MAX_SDIO_PAYLOAD + 4) #define PPP_SOF 0x7E + // Interrupts and registers #define SLAVE_INTR 0 #define SLAVE_REG_REQ 0 @@ -17,3 +19,11 @@ // Requests from host to slave #define REQ_RESET 1 #define REQ_INIT 2 + +struct header { + uint8_t magic; + uint8_t channel; + uint16_t size; +} __attribute__((packed)); + +esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len); diff --git a/components/eppp_link/eppp_sdio_host.c b/components/eppp_link/eppp_sdio_host.c index d22728b9ef..ae909765c2 100644 --- a/components/eppp_link/eppp_sdio_host.c +++ b/components/eppp_link/eppp_sdio_host.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 */ @@ -15,6 +15,7 @@ #include "sdmmc_cmd.h" #include "esp_check.h" #include "eppp_link.h" +#include "eppp_transport.h" #if CONFIG_EPPP_LINK_DEVICE_SDIO_HOST @@ -28,22 +29,23 @@ 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]; +static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PACKET_SIZE]; +static DMA_ATTR uint8_t rcv_buffer[SDIO_PACKET_SIZE]; -esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len) +static esp_err_t eppp_sdio_host_tx_generic(int channel, 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); - } + + struct header *head = (void *)send_buffer; + head->magic = PPP_SOF; + head->channel = channel; + head->size = len; + memcpy(send_buffer + sizeof(struct header), buffer, len); + size_t send_len = SDIO_ALIGN(len + sizeof(struct header)); 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) { @@ -56,6 +58,19 @@ esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len) return ret; } +esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len) +{ + return eppp_sdio_host_tx_generic(0, buffer, len); +} + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + return eppp_sdio_host_tx_generic(channel, buffer, len); +} +#endif + + static esp_err_t request_slave_reset(void) { esp_err_t ret = ESP_OK; @@ -145,15 +160,37 @@ esp_err_t eppp_sdio_host_rx(esp_netif_t *netif) 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); + size_t size_read = SDIO_PACKET_SIZE; + ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PACKET_SIZE, &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); + struct header *head = (void *)rcv_buffer; + if (head->magic != PPP_SOF) { + ESP_LOGE(TAG, "invalid magic %x", head->magic); + break; + } + if (head->channel > NR_OF_CHANNELS) { + ESP_LOGE(TAG, "invalid channel %x", head->channel); + break; + } + if (head->size > SDIO_PAYLOAD || head->size > size_read) { + ESP_LOGE(TAG, "invalid size %x", head->size); + break; + } ESP_LOG_BUFFER_HEXDUMP(TAG, rcv_buffer, size_read, ESP_LOG_VERBOSE); - esp_netif_receive(netif, rcv_buffer, size_read, NULL); + if (head->channel == 0) { + esp_netif_receive(netif, rcv_buffer + sizeof(struct header), head->size, NULL); + } else { +#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT) + struct eppp_handle *h = esp_netif_get_io_driver(netif); + if (h->channel_rx) { + h->channel_rx(netif, head->channel, rcv_buffer + sizeof(struct header), head->size); + } +#endif + } break; } else { ESP_LOGE(TAG, "rx packet error: %08X", ret); diff --git a/components/eppp_link/eppp_sdio_slave.c b/components/eppp_link/eppp_sdio_slave.c index bc971d16dd..2c3a2e642e 100644 --- a/components/eppp_link/eppp_sdio_slave.c +++ b/components/eppp_link/eppp_sdio_slave.c @@ -10,9 +10,9 @@ #include "esp_netif.h" #include "driver/sdio_slave.h" #include "eppp_link.h" +#include "eppp_transport.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 @@ -21,19 +21,18 @@ 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) +static esp_err_t eppp_sdio_host_tx_generic(int channel, 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); - } - + struct header *head = (void *)sdio_slave_tx_buffer; + head->magic = PPP_SOF; + head->channel = channel; + head->size = len; + memcpy(sdio_slave_tx_buffer + sizeof(struct header), buffer, len); + size_t send_len = SDIO_ALIGN(len + sizeof(struct header)); 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) { @@ -44,6 +43,18 @@ esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len) return ESP_OK; } +esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len) +{ + return eppp_sdio_host_tx_generic(0, buffer, len); +} + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + return eppp_sdio_host_tx_generic(channel, buffer, len); +} +#endif + static esp_err_t slave_reset(void) { esp_err_t ret = ESP_OK; @@ -82,7 +93,29 @@ esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif) 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); + struct header *head = (void *)ptr; + if (head->magic != PPP_SOF) { + ESP_LOGE(TAG, "invalid magic %x", head->magic); + return ESP_FAIL; + } + if (head->channel > NR_OF_CHANNELS) { + ESP_LOGE(TAG, "invalid channel %x", head->channel); + return ESP_FAIL; + } + if (head->size > SDIO_PAYLOAD || head->size > length) { + ESP_LOGE(TAG, "invalid size %x", head->size); + return ESP_FAIL; + } + if (head->channel == 0) { + esp_netif_receive(netif, ptr + sizeof(struct header), head->size, NULL); + } else { +#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT) + struct eppp_handle *h = esp_netif_get_io_driver(netif); + if (h->channel_rx) { + h->channel_rx(netif, head->channel, ptr + sizeof(struct header), head->size); + } +#endif + } if (sdio_slave_recv_load_buf(handle) != ESP_OK) { ESP_LOGE(TAG, "Failed to recycle packet buffer"); return ESP_FAIL; diff --git a/components/eppp_link/eppp_spi.c b/components/eppp_link/eppp_spi.c index 2588b5a699..233a8ee010 100644 --- a/components/eppp_link/eppp_spi.c +++ b/components/eppp_link/eppp_spi.c @@ -6,6 +6,7 @@ #include #include #include +#include "sdkconfig.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_check.h" @@ -24,7 +25,8 @@ #define MAX_PAYLOAD 1500 #define MIN_TRIGGER_US 20 -#define SPI_HEADER_MAGIC 0x1234 +#define PPP_SOF 0x7E +#define SPI_HEADER_MAGIC PPP_SOF #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 */ @@ -32,10 +34,12 @@ struct packet { size_t len; uint8_t *data; + int channel; }; struct header { - uint16_t magic; + uint8_t magic; + uint8_t channel; uint16_t size; uint16_t next_size; uint16_t check; @@ -65,12 +69,10 @@ struct eppp_spi { esp_timer_handle_t timer; }; -static esp_err_t transmit(void *h, void *buffer, size_t len) +static esp_err_t transmit_generic(struct eppp_spi *handle, int channel, 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 = { }; + + struct packet buf = { .channel = channel }; uint8_t *current_buffer = buffer; size_t remaining = len; do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform @@ -100,14 +102,25 @@ static esp_err_t transmit(void *h, void *buffer, size_t len) } 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 esp_err_t transmit(void *h, void *buffer, size_t len) +{ + struct eppp_handle *handle = h; + struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);; + return transmit_generic(spi_handle, 0, buffer, len); +} + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + struct eppp_handle *handle = esp_netif_get_io_driver(netif); + struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);; + return transmit_generic(spi_handle, channel, buffer, len); +} +#endif + static void IRAM_ATTR timer_callback(void *arg) { struct eppp_spi *h = arg; @@ -339,6 +352,7 @@ esp_err_t eppp_perform(esp_netif_t *netif) if (h->outbound.len <= h->transaction_size && allow_test_tx == false) { // sending outbound head->size = h->outbound.len; + head->channel = h->outbound.channel; if (h->outbound.len > 0) { memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len); free(h->outbound.data); @@ -355,6 +369,7 @@ esp_err_t eppp_perform(esp_netif_t *netif) } else { // outbound is bigger, need to transmit in another transaction (keep this empty) head->size = 0; + head->channel = 0; } next_tx_size = head->next_size = h->outbound.len; head->magic = SPI_HEADER_MAGIC; @@ -367,17 +382,25 @@ esp_err_t eppp_perform(esp_netif_t *netif) } 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) { + if (check != head->check || head->magic != SPI_HEADER_MAGIC || head->channel > NR_OF_CHANNELS) { 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"); + ESP_LOGE(TAG, "Wrong checksum, magic, or channel: %x %x %x", check, head->magic, head->channel); 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); + if (head->channel == 0) { + esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL); + } else { +#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT) + if (h->parent.channel_rx) { + h->parent.channel_rx(netif, head->channel, in_buf + sizeof(struct header), head->size); + } +#endif + } } h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size); return ESP_OK; @@ -415,6 +438,9 @@ eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config) 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"); +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT + h->parent.channel_tx = transmit_channel; +#endif 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)); diff --git a/components/eppp_link/eppp_transport.h b/components/eppp_link/eppp_transport.h index b911e67b60..ad3b5d821a 100644 --- a/components/eppp_link/eppp_transport.h +++ b/components/eppp_link/eppp_transport.h @@ -5,6 +5,13 @@ */ #pragma once #include "esp_netif_types.h" +#include "sdkconfig.h" + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +#define NR_OF_CHANNELS CONFIG_EPPP_LINK_NR_OF_CHANNELS +#else +#define NR_OF_CHANNELS 1 +#endif struct eppp_handle { esp_netif_driver_base_t base; @@ -12,6 +19,11 @@ struct eppp_handle { bool stop; bool exited; bool netif_stop; +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT + eppp_channel_fn_t channel_tx; + eppp_channel_fn_t channel_rx; + void* context; +#endif }; 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 index 2101e93f38..75283aa581 100644 --- a/components/eppp_link/eppp_uart.c +++ b/components/eppp_link/eppp_uart.c @@ -31,19 +31,19 @@ struct eppp_uart { struct header { uint8_t magic; + uint8_t channel; uint8_t check; uint16_t size; } __attribute__((packed)); -static esp_err_t transmit(void *h, void *buffer, size_t len) +static esp_err_t transmit_generic(struct eppp_uart *handle, int channel, 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->channel = channel; head->size = len; head->check = (0xFF & len) ^ (len >> 8); memcpy(out_buf + sizeof(struct header), buffer, len); @@ -56,6 +56,22 @@ static esp_err_t transmit(void *h, void *buffer, size_t len) return ESP_OK; } +static esp_err_t transmit(void *h, void *buffer, size_t len) +{ + struct eppp_handle *handle = h; + struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent); + return transmit_generic(uart_handle, 0, buffer, len); +} + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len) +{ + struct eppp_handle *handle = esp_netif_get_io_driver(netif); + struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent); + return transmit_generic(uart_handle, channel, buffer, len); +} +#endif + static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *config) { h->uart_port = config->port; @@ -122,6 +138,7 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava // Check if we have the complete packet uint16_t payload_size = head->size; + int channel = head->channel; size_t total_packet_size = sizeof(struct header) + payload_size; if (payload_size > MAX_PAYLOAD) { @@ -136,7 +153,15 @@ static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t ava } // Got a complete packet, pass it to network - esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL); + if (channel == 0) { + esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL); + } else { +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT + if (h->parent.channel_rx) { + h->parent.channel_rx(netif, channel, in_buf + buf_start + sizeof(struct header), payload_size); + } +#endif + } // Advance start pointer past this packet buf_start += total_packet_size; @@ -237,6 +262,9 @@ eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config) 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"); +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT + h->parent.channel_tx = transmit_channel; +#endif h->parent.base.post_attach = post_attach; ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART"); return &h->parent; diff --git a/components/eppp_link/examples/host/main/CMakeLists.txt b/components/eppp_link/examples/host/main/CMakeLists.txt index 0198ddd323..a3fc7614fb 100644 --- a/components/eppp_link/examples/host/main/CMakeLists.txt +++ b/components/eppp_link/examples/host/main/CMakeLists.txt @@ -1,2 +1,6 @@ +if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL) + set(wifi_over_channels channel_wifi_station.c) +endif() idf_component_register(SRCS app_main.c register_iperf.c - INCLUDE_DIRS ".") + ${wifi_over_channels} + INCLUDE_DIRS ".") diff --git a/components/eppp_link/examples/host/main/Kconfig.projbuild b/components/eppp_link/examples/host/main/Kconfig.projbuild index f594a728c8..e3a53fe5a8 100644 --- a/components/eppp_link/examples/host/main/Kconfig.projbuild +++ b/components/eppp_link/examples/host/main/Kconfig.projbuild @@ -106,4 +106,14 @@ menu "Example Configuration" help Baudrate used by the PPP over UART + config EXAMPLE_WIFI_OVER_EPPP_CHANNEL + bool "Use WiFi over EPPP channel" + default n + depends on EPPP_LINK_CHANNELS_SUPPORT && ESP_WIFI_REMOTE_ENABLED + help + Enable this option to use WiFi over EPPP channel. + If this option is enabled, the example will only start the Wi-Fi driver, + but the Wi-Fi netif will reside on client's end and will channel + the Rx and Tx data via EPPP channels. + endmenu diff --git a/components/eppp_link/examples/host/main/app_main.c b/components/eppp_link/examples/host/main/app_main.c index c147d87eeb..b082137eac 100644 --- a/components/eppp_link/examples/host/main/app_main.c +++ b/components/eppp_link/examples/host/main/app_main.c @@ -14,6 +14,7 @@ #include "esp_netif.h" #include "eppp_link.h" #include "esp_log.h" +#include "esp_check.h" #include "mqtt_client.h" #include "console_ping.h" @@ -88,6 +89,7 @@ static void mqtt_app_start(void) } #endif // MQTT +void station_over_eppp_channel(void *arg); void app_main(void) { @@ -156,6 +158,9 @@ void app_main(void) // start console REPL ESP_ERROR_CHECK(console_cmd_start()); +#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL + station_over_eppp_channel(eppp_netif); +#endif #if CONFIG_EXAMPLE_MQTT mqtt_app_start(); #endif diff --git a/components/eppp_link/examples/host/main/channel_wifi_station.c b/components/eppp_link/examples/host/main/channel_wifi_station.c new file mode 100644 index 0000000000..87045dc5c3 --- /dev/null +++ b/components/eppp_link/examples/host/main/channel_wifi_station.c @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#include +#include "esp_system.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "eppp_link.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_wifi_remote.h" + +#define CHAT_CHANNEL 1 +#define WIFI_CHANNEL 2 + +typedef enum { + UNKNOWN, + HELLO, + START, + ERROR, +} state_t; + +typedef struct context { + eppp_channel_fn_t transmit; + EventGroupHandle_t flags; + state_t state; + esp_netif_t *eppp; +} context_t; + +#define HELLO_BIT BIT0 +#define START_BIT BIT1 +#define CONNECT_BIT BIT2 +#define SERVER_UP_BIT BIT3 + +#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | SERVER_UP_BIT) + +static uint8_t s_wifi_mac_addr[6] = { 0 }; +static const char *TAG = "eppp_host_example_with_channels"; + +esp_netif_t* esp_wifi_remote_create_default_sta(void); + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + ESP_LOGI(TAG, "IP event_handler: event_base=%s event_id=%d", event_base, event_id); + if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; + ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip)); + } +} + +esp_err_t esp_wifi_remote_get_mac(wifi_interface_t ifx, uint8_t mac[6]) +{ + if (ifx != WIFI_IF_STA) { + return ESP_ERR_INVALID_STATE; + + } + for (int i = 0; i < sizeof(s_wifi_mac_addr); i++) { + if (s_wifi_mac_addr[i] == 0) { + return ESP_ERR_INVALID_STATE; + } + } + memcpy(mac, s_wifi_mac_addr, sizeof(s_wifi_mac_addr)); + return ESP_OK; +} + +static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len) +{ + context_t *ctx = eppp_get_context(netif); + if (nr == CHAT_CHANNEL) { + ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer); + const char hello[] = "Hello client"; + const char mac[] = "MAC: "; + const char connected[] = "Connected"; + const char server_up[] = "Server up"; + size_t mac_len = 5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */; + if (len == sizeof(server_up) && memcmp(buffer, server_up, len) == 0) { + if (ctx->state == UNKNOWN) { + ESP_LOGI(TAG, "Server is up"); + ctx->state = HELLO; + } else { + ESP_LOGE(TAG, "Received server up in unexpected state %d", ctx->state); + ctx->state = ERROR; + } + xEventGroupSetBits(ctx->flags, SERVER_UP_BIT); + } else if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) { + if (ctx->state == HELLO) { + xEventGroupSetBits(ctx->flags, HELLO_BIT); + } else { + ESP_LOGE(TAG, "Received hello in unexpected state %d", ctx->state); + ctx->state = ERROR; + } + } else if (len == mac_len && memcmp(buffer, mac, 5) == 0) { + if (ctx->state == HELLO) { + uint8_t mac_addr[6]; + sscanf((char *)buffer + 5, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8, + &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]); + ESP_LOGI(TAG, "Parsed MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + memcpy(s_wifi_mac_addr, mac_addr, sizeof(s_wifi_mac_addr)); + xEventGroupSetBits(ctx->flags, START_BIT); + } else { + ESP_LOGE(TAG, "Received MAC in unexpected state %d", ctx->state); + ctx->state = ERROR; + } + } else if (len == sizeof(connected) && memcmp(buffer, connected, len) == 0) { + if (ctx->state == START) { + xEventGroupSetBits(ctx->flags, CONNECT_BIT); + } else { + ESP_LOGE(TAG, "Received connected in unexpected state %d", ctx->state); + ctx->state = ERROR; + } + } + } else if (nr == WIFI_CHANNEL) { + ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len); + ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE); + return esp_wifi_remote_channel_rx(ctx->eppp, buffer, NULL, len); + } else { + ESP_LOGE(TAG, "Incorrect channel number %d", nr); + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t wifi_transmit(void *h, void *buffer, size_t len) +{ + esp_netif_t *eppp = (esp_netif_t *)h; + context_t *ctx = eppp_get_context(eppp); + ESP_LOG_BUFFER_HEXDUMP("wifi-transmit", buffer, len, ESP_LOG_VERBOSE); + return ctx->transmit(eppp, WIFI_CHANNEL, buffer, len); +} + +void esp_netif_destroy_wifi_remote(void *esp_netif); + +void station_over_eppp_channel(void *arg) +{ + __attribute__((unused)) esp_err_t ret; + esp_netif_t *wifi = NULL; + context_t ctx = { + .transmit = NULL, + .flags = NULL, + .state = UNKNOWN, + .eppp = (esp_netif_t *)arg + }; + ESP_GOTO_ON_FALSE(ctx.eppp != NULL, ESP_FAIL, err, TAG, "Incorrect EPPP netif"); + ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group"); + ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels"); + ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set"); + ESP_GOTO_ON_ERROR(esp_wifi_remote_channel_set(WIFI_IF_STA, ctx.eppp, wifi_transmit), err, TAG, "Failed to set wifi channel tx function"); + esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, &ctx); + + while (1) { + EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000)); + if (bits & HELLO_BIT) { + ESP_LOGI(TAG, "Hello done"); + if (wifi == NULL) { + wifi = esp_wifi_remote_create_default_sta(); + } + const char command[] = "Get MAC"; + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)command, sizeof(command)); + } else if (bits & START_BIT) { + ctx.state = START; + ESP_LOGI(TAG, "Starting WIFI"); + esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_START, NULL, 0, 0); + } else if (bits & CONNECT_BIT) { + ESP_LOGI(TAG, "WIFI connected"); + esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_CONNECTED, NULL, 0, 0); + } else if ((bits & SERVER_UP_BIT) == SERVER_UP_BIT || ctx.state != START) { + if (ctx.state == ERROR) { + esp_netif_destroy_wifi_remote(wifi); + wifi = NULL; + ESP_LOGI(TAG, "WiFi netif has been destroyed"); + } + const char hello[] = "Hello server"; + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello)); + ctx.state = HELLO; + } + } + +err: + vTaskDelete(NULL); +} diff --git a/components/eppp_link/examples/host/main/idf_component.yml b/components/eppp_link/examples/host/main/idf_component.yml index eeaef0415d..2a3dfbd833 100644 --- a/components/eppp_link/examples/host/main/idf_component.yml +++ b/components/eppp_link/examples/host/main/idf_component.yml @@ -1,7 +1,7 @@ dependencies: - espressif/iperf-cmd: "^0.1.1" + espressif/iperf-cmd: ^0.1.1 espressif/eppp_link: - version: "*" - override_path: "../../.." + version: '*' + override_path: ../../.. console_cmd_ping: - version: "*" + version: '*' diff --git a/components/eppp_link/examples/slave/main/CMakeLists.txt b/components/eppp_link/examples/slave/main/CMakeLists.txt index 12c5007a37..23fd9bde17 100644 --- a/components/eppp_link/examples/slave/main/CMakeLists.txt +++ b/components/eppp_link/examples/slave/main/CMakeLists.txt @@ -1,2 +1,6 @@ -idf_component_register(SRCS "eppp_slave.c" - INCLUDE_DIRS ".") +if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL) + set(wifi_over_channels channel_wifi_station.c) +endif() +idf_component_register(SRCS eppp_slave.c + ${wifi_over_channels} + INCLUDE_DIRS ".") diff --git a/components/eppp_link/examples/slave/main/Kconfig.projbuild b/components/eppp_link/examples/slave/main/Kconfig.projbuild index e17f711b43..532566d303 100644 --- a/components/eppp_link/examples/slave/main/Kconfig.projbuild +++ b/components/eppp_link/examples/slave/main/Kconfig.projbuild @@ -98,4 +98,14 @@ menu "Example Configuration" help Baudrate used by the PPP over UART + config EXAMPLE_WIFI_OVER_EPPP_CHANNEL + bool "Use WiFi over EPPP channel" + default n + depends on EPPP_LINK_CHANNELS_SUPPORT + help + Enable this option to use WiFi over EPPP channel. + If this option is enabled, the example will only start the Wi-Fi driver, + but the Wi-Fi netif will reside on client's end and will channel + the Rx and Tx data via EPPP channels. + endmenu diff --git a/components/eppp_link/examples/slave/main/channel_wifi_station.c b/components/eppp_link/examples/slave/main/channel_wifi_station.c new file mode 100644 index 0000000000..9e41364c18 --- /dev/null +++ b/components/eppp_link/examples/slave/main/channel_wifi_station.c @@ -0,0 +1,169 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#include +#include "esp_system.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "eppp_link.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_private/wifi.h" + +#define CHAT_CHANNEL 1 +#define WIFI_CHANNEL 2 + +typedef enum { + UNKNOWN, + HELLO, + START, + ERROR, +} state_t; + +typedef struct context { + eppp_channel_fn_t transmit; + EventGroupHandle_t flags; + state_t state; + esp_netif_t *eppp; +} context_t; + +#define HELLO_BIT BIT0 +#define START_BIT BIT1 +#define CONNECT_BIT BIT2 +#define DISCONNECT_BIT BIT3 + +#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | DISCONNECT_BIT) + +static const char *TAG = "eppp_host_example_with_channels"; +static context_t *s_eppp_channel_ctx = NULL; + +static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len) +{ + context_t *ctx = eppp_get_context(netif); + if (nr == CHAT_CHANNEL) { + ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer); + const char hello[] = "Hello server"; + const char mac[] = "Get MAC"; + if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) { + if (ctx->state == HELLO) { + xEventGroupSetBits(ctx->flags, HELLO_BIT); + } else { + ctx->state = ERROR; + } + } else if (len == sizeof(mac) && memcmp(buffer, mac, 5) == 0) { + if (ctx->state == HELLO) { + xEventGroupSetBits(ctx->flags, START_BIT); + } else { + ctx->state = ERROR; + } + } + } else if (nr == WIFI_CHANNEL) { + ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len); + ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE); + return esp_wifi_internal_tx(WIFI_IF_STA, buffer, len); + } else { + ESP_LOGE(TAG, "Incorrect channel number %d", nr); + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t wifi_receive(void *buffer, uint16_t len, void *eb) +{ + s_eppp_channel_ctx->transmit(s_eppp_channel_ctx->eppp, WIFI_CHANNEL, buffer, len); + esp_wifi_internal_free_rx_buffer(eb); + return ESP_OK; +} + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + context_t *ctx = arg; + ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%d", event_base, event_id); + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + ESP_LOGI(TAG, "WIFI start event"); + esp_wifi_connect(); + xEventGroupSetBits(ctx->flags, CONNECT_BIT); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + ESP_LOGI(TAG, "connect to the AP fail"); + xEventGroupSetBits(ctx->flags, DISCONNECT_BIT); + } +} + + +static void init_wifi_driver(context_t *ctx) +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, + event_handler, ctx)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_ESP_WIFI_SSID, + .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)); +} + +void station_over_eppp_channel(void *arg) +{ + __attribute__((unused)) esp_err_t ret; + context_t ctx = { + .transmit = NULL, + .flags = NULL, + .state = UNKNOWN, + .eppp = (esp_netif_t *)arg + }; + ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group"); + ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels"); + ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set"); + init_wifi_driver(&ctx); + + while (1) { + EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000)); + if (bits & HELLO_BIT) { + ESP_LOGI(TAG, "Hello from client received"); + const char hello[] = "Hello client"; + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello)); + } else if (bits & START_BIT) { + ctx.state = START; + ESP_LOGI(TAG, "Starting WIFI"); + uint8_t mac[6]; + if (esp_wifi_get_mac(WIFI_IF_STA, mac) != ESP_OK) { + ESP_LOGE(TAG, "esp_wifi_get_mac failed"); + ctx.state = ERROR; + continue; + } + char mac_data[5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */]; + sprintf(mac_data, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + ESP_LOGI(TAG, "Sending MAC: %.*s", (int)sizeof(mac_data), mac_data); + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)mac_data, sizeof(mac_data)); + ret = esp_wifi_start(); + ESP_LOGI(TAG, "WIFI start result: %d", ret); + s_eppp_channel_ctx = &ctx; + esp_wifi_internal_reg_rxcb(WIFI_IF_STA, wifi_receive); + } else if (bits & CONNECT_BIT) { + ESP_LOGI(TAG, "WIFI connected"); + const char connected[] = "Connected"; + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)connected, sizeof(connected)); + } else if (bits & DISCONNECT_BIT) { + const char disconnected[] = "Disconnected"; + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)disconnected, sizeof(disconnected)); + } else if (ctx.state != START) { + ctx.state = HELLO; + const char up[] = "Server up"; + esp_wifi_disconnect(); + esp_wifi_stop(); + ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)up, sizeof(up)); + } + } + +err: + vTaskDelete(NULL); +} diff --git a/components/eppp_link/examples/slave/main/eppp_slave.c b/components/eppp_link/examples/slave/main/eppp_slave.c index ff6dd6e056..a35e47b450 100644 --- a/components/eppp_link/examples/slave/main/eppp_slave.c +++ b/components/eppp_link/examples/slave/main/eppp_slave.c @@ -11,12 +11,14 @@ #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_check.h" #include "nvs_flash.h" #include "eppp_link.h" +#include "inttypes.h" static const char *TAG = "eppp_slave"; -#if CONFIG_SOC_WIFI_SUPPORTED +#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL) /* FreeRTOS event group to signal when we are connected*/ static EventGroupHandle_t s_wifi_event_group; @@ -27,12 +29,13 @@ static EventGroupHandle_t s_wifi_event_group; #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 - static int s_retry_num = 0; static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%" PRIi32, event_base, event_id); if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + ESP_LOGI(TAG, "WIFI start event"); esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { @@ -107,12 +110,15 @@ void init_network_interface(void) } #else +// If the SoC does not have WiFi capabilities, we can initialize a different network interface, this function is a placeholder for that purpose. +// This function is also a no-op if EXAMPLE_WIFI_OVER_EPPP_CHANNEL==1, since the Wi-Fi network interface will live on the other peer (on the host side). void init_network_interface(void) { - // placeholder to initialize any other network interface if WiFi is not available } -#endif // SoC WiFi capable chip +#endif // SoC WiFi capable chip || WiFi over EPPP channel + +void station_over_eppp_channel(void *arg); void app_main(void) { @@ -153,5 +159,9 @@ void app_main(void) ESP_LOGE(TAG, "Failed to setup connection"); return ; } +#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL + station_over_eppp_channel(eppp_netif); +#else ESP_ERROR_CHECK(esp_netif_napt_enable(eppp_netif)); +#endif // CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL } diff --git a/components/eppp_link/idf_component.yml b/components/eppp_link/idf_component.yml index 1827383a83..443f6d2a6d 100644 --- a/components/eppp_link/idf_component.yml +++ b/components/eppp_link/idf_component.yml @@ -1,4 +1,4 @@ -version: 0.3.1 +version: 1.0.0 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: diff --git a/components/eppp_link/include/eppp_link.h b/components/eppp_link/include/eppp_link.h index ca51e23b7c..3df11c4d82 100644 --- a/components/eppp_link/include/eppp_link.h +++ b/components/eppp_link/include/eppp_link.h @@ -5,6 +5,10 @@ */ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + #define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1) #define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2) @@ -110,7 +114,6 @@ typedef enum eppp_transport { typedef struct eppp_config_t { eppp_transport_t transport; - struct eppp_config_spi_s { int host; bool is_master; @@ -203,3 +206,15 @@ void eppp_netif_deinit(esp_netif_t *netif); * - ESP_ERR_TIMEOUT indicates that the operation was requested to stop */ esp_err_t eppp_perform(esp_netif_t *netif); + +#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT +typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len); + +esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context); + +void* eppp_get_context(esp_netif_t *netif); +#endif // CONFIG_EPPP_LINK_CHANNELS_SUPPORT + +#ifdef __cplusplus +} +#endif