Files
esp-protocols/components/eppp_link/eppp_uart.c
David Cermak 2ff150c310 feat(eppp): Add support for TUN interface
* Implement factory pattern
* Make netif type configurable (PPP vs. TUN)
* Use ICMP for TUN netif connection
* Relax deps criteria (mainly for Ethernet)use the tun feature
2025-06-12 11:59:17 +02:00

253 lines
8.6 KiB
C

/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#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);
}