mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(esp_netif): integrate L2TAP with Ethernet sublayer
Route L2TAP TX/RX through the iodriver provider, support offset RX buffers in ethernetif, and extend L2TAP tests for sublayer configs.
This commit is contained in:
@@ -66,7 +66,6 @@ menu "ESP NETIF Adapter"
|
||||
|
||||
config ESP_NETIF_L2_TAP
|
||||
bool "Enable netif L2 TAP support"
|
||||
select ETH_TRANSMIT_MUTEX
|
||||
help
|
||||
A user program can read/write link layer (L2) frames from/to ESP TAP device.
|
||||
The ESP TAP device can be currently associated only with Ethernet physical interfaces.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
|
||||
@@ -20,7 +21,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The IO driver / IO driver provider vtable types are a generic concept owned by esp_eth for example
|
||||
* (see esp_private/esp_eth_sublayer_iodriver.h).
|
||||
*/
|
||||
typedef void *l2tap_iodriver_handle;
|
||||
typedef void *l2tap_iodriver_provider_handle;
|
||||
|
||||
/**
|
||||
* @brief L2Tap VFS config parameters
|
||||
@@ -30,6 +36,10 @@ typedef struct {
|
||||
const char* base_path; /*!< vfs base path */
|
||||
} l2tap_vfs_config_t;
|
||||
|
||||
/**
|
||||
* @brief L2 TAP ioctl options
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
L2TAP_S_RCV_FILTER, /*!< Set Ethertype filter, frames with this type to be passed to the file descriptor. */
|
||||
L2TAP_G_RCV_FILTER, /*!< Get current Ethertype filter. */
|
||||
@@ -40,6 +50,29 @@ typedef enum {
|
||||
L2TAP_S_TIMESTAMP_EN, /*!< Enables the hardware Time Stamping (TS) processing by the file descriptor. TS needs to be supported by hardware and enabled in the IO driver. */
|
||||
} l2tap_ioctl_opt_t;
|
||||
|
||||
/**
|
||||
* @brief Hardware timestamp for L2 TAP (seconds + nanoseconds).
|
||||
*
|
||||
* Used for RX metadata passed to esp_vfs_l2tap_eth_filter_frame() and for TX completion when
|
||||
* ``L2TAP_S_TIMESTAMP_EN`` is enabled.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t sec; /*!< Seconds */
|
||||
uint32_t nsec; /*!< Nanoseconds (typically 0..999999999 for PTP-style HW time) */
|
||||
} l2tap_timestamp_t;
|
||||
|
||||
/**
|
||||
* @brief Extra information about received Ethernet frame
|
||||
*
|
||||
* Used for RX metadata passed to esp_vfs_l2tap_eth_filter_frame().
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *l2_buffer; /*!< Pointer to the starting address of the L2 buffer (optional) */
|
||||
l2tap_timestamp_t *hw_ts; /*!< Pointer to the hardware timestamp (optional) */
|
||||
} l2tap_eth_filter_info_t;
|
||||
|
||||
/**
|
||||
* @brief Information Record (IREC) Header Type indicates expected type of Header Data
|
||||
*
|
||||
@@ -121,11 +154,11 @@ esp_err_t esp_vfs_l2tap_intf_unregister(const char *base_path);
|
||||
* @param driver_handle handle of driver at which the frame was received
|
||||
* @param buff received L2 frame
|
||||
* @param size input length of the L2 frame which is set to 0 when frame is filtered into L2 TAP
|
||||
* @param info extra information about received Ethernet frame
|
||||
* @param info optional per-frame metadata from the IO driver; may be NULL.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK is always returned
|
||||
*/
|
||||
esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, void *buff, size_t *size, void *info);
|
||||
esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, void *buff, size_t *size, l2tap_eth_filter_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Wrapper over L2 TAP filter function to ensure backward compatibility.
|
||||
@@ -140,6 +173,33 @@ esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, vo
|
||||
*/
|
||||
#define esp_vfs_l2tap_eth_filter(drv_hndl, buf, size) esp_vfs_l2tap_eth_filter_frame(drv_hndl, buf, size, NULL)
|
||||
|
||||
/**
|
||||
* @brief Register an iodriver provider handle with L2 TAP.
|
||||
*
|
||||
* The registered handle becomes visible to all open L2 TAP file descriptors.
|
||||
* Safe to call from any task context. Must not be called from an ISR.
|
||||
*
|
||||
* @param provider Opaque iodriver provider handle (e.g. from esp_eth_sublayer_new()).
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if provider is NULL
|
||||
* - ESP_ERR_INVALID_STATE if provider is already registered
|
||||
* - ESP_ERR_NO_MEM if memory allocation failed
|
||||
*/
|
||||
esp_err_t esp_vfs_l2tap_iodriver_provider_register(l2tap_iodriver_provider_handle provider);
|
||||
|
||||
/**
|
||||
* @brief Unregister a previously registered iodriver provider handle from L2 TAP.
|
||||
*
|
||||
* Safe to call from any task context. Must not be called from an ISR.
|
||||
*
|
||||
* @param provider Handle to remove.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if provider is NULL
|
||||
* - ESP_ERR_NOT_FOUND if provider was not registered
|
||||
*/
|
||||
esp_err_t esp_vfs_l2tap_iodriver_provider_unregister(l2tap_iodriver_provider_handle provider);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* SPDX-FileContributor: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/**
|
||||
* @file
|
||||
@@ -114,7 +114,7 @@ static err_t ethernet_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
* @param h lwip network interface structure (struct netif) for this ethernetif
|
||||
* @param buffer ethernet buffer
|
||||
* @param len length of buffer
|
||||
* @param l2_buff Placeholder for a separate L2 buffer. Unused for ethernet interface
|
||||
* @param l2_buff Optional original allocation for RX path (when @a buffer is offset into it); if NULL, @a buffer is freed
|
||||
*/
|
||||
esp_err_t ethernetif_input(void *h, void *buffer, size_t len, void *l2_buff)
|
||||
{
|
||||
@@ -124,15 +124,15 @@ esp_err_t ethernetif_input(void *h, void *buffer, size_t len, void *l2_buff)
|
||||
|
||||
if (unlikely(buffer == NULL || !netif_is_up(netif))) {
|
||||
if (buffer) {
|
||||
esp_netif_free_rx_buffer(esp_netif, buffer);
|
||||
esp_netif_free_rx_buffer(esp_netif, l2_buff ? l2_buff : buffer);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* allocate custom pbuf to hold */
|
||||
p = esp_pbuf_allocate(esp_netif, buffer, len, buffer);
|
||||
p = esp_pbuf_allocate(esp_netif, buffer, len, l2_buff ? l2_buff : buffer);
|
||||
if (p == NULL) {
|
||||
esp_netif_free_rx_buffer(esp_netif, buffer);
|
||||
esp_netif_free_rx_buffer(esp_netif, l2_buff ? l2_buff : buffer);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
/* full packet send to tcpip_thread to process */
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
menu "L2 TAP VFS test app"
|
||||
|
||||
config L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
bool "Use Ethernet netif sublayer instead of classic glue"
|
||||
default n
|
||||
depends on ETH_SUBLAYER_SUPPORT
|
||||
help
|
||||
When set, the test attaches esp-netif with esp_eth_sublayer_new() /
|
||||
esp_eth_sublayer_del() (802.1Q demux path). When unset, the classic
|
||||
esp_eth_new_netif_glue() / esp_eth_del_netif_glue() layer is used.
|
||||
|
||||
endmenu
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -52,8 +52,14 @@ typedef struct {
|
||||
esp_netif_t *eth_netif;
|
||||
esp_eth_mac_t *mac;
|
||||
esp_eth_phy_t *phy;
|
||||
void *glue;
|
||||
#ifdef CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
esp_eth_sublayer_handle_t eth_sub;
|
||||
esp_eth_sublayer_vlan_handle_t vlan_untagged;
|
||||
#else
|
||||
void *netif_io_driver;
|
||||
#endif
|
||||
esp_eth_handle_t eth_handle;
|
||||
EventGroupHandle_t eth_event_group;
|
||||
} test_vfs_eth_network_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -175,18 +181,24 @@ static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
|
||||
/**
|
||||
* @brief Initializes Ethernet interface and starts driver (internal EMAC & IP101 PHY)
|
||||
*
|
||||
* @param init_netif true to create and attach an esp_netif; false to skip netif (L2TAP via driver handle)
|
||||
*/
|
||||
static void ethernet_init(test_vfs_eth_network_t *network_hndls)
|
||||
static void ethernet_init(test_vfs_eth_network_t *network_hndls, bool init_netif)
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
EventGroupHandle_t eth_event_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_group != NULL);
|
||||
|
||||
test_case_uses_tcpip();
|
||||
memset(network_hndls, 0, sizeof(*network_hndls));
|
||||
network_hndls->eth_event_group = eth_event_group;
|
||||
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
// create TCP/IP netif
|
||||
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
||||
network_hndls->eth_netif = esp_netif_new(&netif_cfg);
|
||||
if (init_netif) {
|
||||
test_case_uses_tcpip();
|
||||
// create TCP/IP netif
|
||||
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
||||
network_hndls->eth_netif = esp_netif_new(&netif_cfg);
|
||||
}
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
@@ -201,11 +213,30 @@ static void ethernet_init(test_vfs_eth_network_t *network_hndls)
|
||||
// install Ethernet driver
|
||||
TEST_ESP_OK(esp_eth_driver_install(ð_config, &network_hndls->eth_handle));
|
||||
// combine driver with netif
|
||||
network_hndls->glue = esp_eth_new_netif_glue(network_hndls->eth_handle);
|
||||
TEST_ESP_OK(esp_netif_attach(network_hndls->eth_netif, network_hndls->glue));
|
||||
#ifdef CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
ESP_LOGI(TAG, "Using Ethernet Sublayer%s", init_netif ? "" : " without esp_netif");
|
||||
esp_eth_sublayer_config_t sub_config = {
|
||||
.eth_handle = network_hndls->eth_handle,
|
||||
};
|
||||
TEST_ESP_OK(esp_eth_sublayer_new(&sub_config, &network_hndls->eth_sub));
|
||||
// Create untagged Ethernet interface
|
||||
TEST_ESP_OK(esp_eth_sublayer_vlan_add(network_hndls->eth_sub, ESP_ETH_SUBLAYER_UNTAGGED_VID,
|
||||
&network_hndls->vlan_untagged));
|
||||
if (init_netif) {
|
||||
TEST_ESP_OK(esp_netif_attach(network_hndls->eth_netif, network_hndls->vlan_untagged));
|
||||
}
|
||||
#else
|
||||
if (init_netif) {
|
||||
ESP_LOGI(TAG, "Using Ethernet Netif Glue");
|
||||
network_hndls->netif_io_driver = esp_eth_new_netif_glue(network_hndls->eth_handle);
|
||||
TEST_ESP_OK(esp_netif_attach(network_hndls->eth_netif, network_hndls->netif_io_driver));
|
||||
}
|
||||
#endif // CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
// register user defined event handlers
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group));
|
||||
if (init_netif) {
|
||||
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group));
|
||||
}
|
||||
|
||||
// set PHY loopback mode
|
||||
bool loopback_en = true;
|
||||
@@ -227,13 +258,27 @@ static void ethernet_init(test_vfs_eth_network_t *network_hndls)
|
||||
static void ethernet_deinit(test_vfs_eth_network_t *network_hndls)
|
||||
{
|
||||
TEST_ESP_OK(esp_eth_stop(network_hndls->eth_handle));
|
||||
TEST_ESP_OK(esp_eth_del_netif_glue(network_hndls->glue));
|
||||
EventBits_t stop_bits = xEventGroupWaitBits(network_hndls->eth_event_group, ETH_STOP_BIT, pdTRUE, pdTRUE,
|
||||
pdMS_TO_TICKS(1000));
|
||||
TEST_ASSERT((stop_bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
#ifdef CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
// delete sublayer, it also removes all VLANs
|
||||
TEST_ESP_OK(esp_eth_sublayer_del(network_hndls->eth_sub));
|
||||
#else
|
||||
if (network_hndls->netif_io_driver) {
|
||||
TEST_ESP_OK(esp_eth_del_netif_glue(network_hndls->netif_io_driver));
|
||||
}
|
||||
#endif // CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
esp_eth_driver_uninstall(network_hndls->eth_handle);
|
||||
TEST_ESP_OK(network_hndls->phy->del(network_hndls->phy));
|
||||
TEST_ESP_OK(network_hndls->mac->del(network_hndls->mac));
|
||||
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
|
||||
if (network_hndls->eth_netif) {
|
||||
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
|
||||
esp_netif_destroy(network_hndls->eth_netif);
|
||||
}
|
||||
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
||||
esp_netif_destroy(network_hndls->eth_netif);
|
||||
vEventGroupDelete(network_hndls->eth_event_group);
|
||||
network_hndls->eth_event_group = NULL;
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
}
|
||||
|
||||
@@ -297,8 +342,6 @@ TEST_CASE("esp32 l2tap - vfs register", "[ethernet]")
|
||||
ESP_LOGI(TAG, "Verify that L2 TAP VFS can be registered only once...");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_vfs_l2tap_intf_register(NULL));
|
||||
|
||||
//ethernet_init(ð_network_hndls);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", O_NONBLOCK);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
@@ -408,7 +451,7 @@ TEST_CASE("esp32 l2tap - open/close", "[ethernet]")
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
open_close_task_ctrl_t task_control;
|
||||
task_control.sem = xSemaphoreCreateBinary();
|
||||
@@ -498,7 +541,7 @@ TEST_CASE("esp32 l2tap - non blocking read", "[ethernet]")
|
||||
int loop_cnt = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", O_NONBLOCK);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -527,6 +570,7 @@ TEST_CASE("esp32 l2tap - non blocking read", "[ethernet]")
|
||||
|
||||
// Verify the read does not block
|
||||
while (loop_cnt < 100) {
|
||||
errno = 0;
|
||||
if ((n = read(eth_tap_fd, in_buffer, IN_BUFFER_SIZE)) > 0) {
|
||||
ESP_LOG_BUFFER_HEX(TAG, in_buffer, n);
|
||||
ESP_LOGI(TAG, "recv test string: %s", ((test_vfs_eth_tap_msg_t *)in_buffer)->str);
|
||||
@@ -594,8 +638,8 @@ TEST_CASE("esp32 l2tap - non blocking read", "[ethernet]")
|
||||
FD_SET(eth_tap_fd, &rfds);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, select(eth_tap_fd + 1, &rfds, NULL, NULL, &tv));
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
|
||||
errno = 0;
|
||||
n = read(eth_tap_fd, in_buffer, IN_BUFFER_SIZE);
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
TEST_ASSERT_EQUAL(-1, n);
|
||||
@@ -618,7 +662,7 @@ TEST_CASE("esp32 l2tap - blocking read", "[ethernet]")
|
||||
int loop_cnt = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -678,7 +722,7 @@ TEST_CASE("esp32 l2tap - write", "[ethernet]")
|
||||
int eth_tap_fd;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -703,6 +747,7 @@ TEST_CASE("esp32 l2tap - write", "[ethernet]")
|
||||
esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &test_msg.header.dest.addr);
|
||||
// set different Ethernet type than the fd is configured to
|
||||
test_msg.header.type = htons(ETH_FILTER_LE + 10);
|
||||
errno = 0;
|
||||
TEST_ASSERT_EQUAL(-1, write(eth_tap_fd, &test_msg, sizeof(test_msg)));
|
||||
TEST_ASSERT_EQUAL(EBADMSG, errno);
|
||||
|
||||
@@ -797,7 +842,7 @@ TEST_CASE("esp32 l2tap - read/write multiple fd's used by multiple tasks", "[eth
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
task_info_t task_info[NUM_OF_TASKS];
|
||||
for (int i = 0; i < NUM_OF_TASKS; i++) {
|
||||
@@ -828,7 +873,7 @@ TEST_CASE("esp32 l2tap - time stamping", "[ethernet]")
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
int eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -904,9 +949,11 @@ TEST_CASE("esp32 l2tap - time stamping", "[ethernet]")
|
||||
ESP_LOGI(TAG, "Verify response when trying to write/read in standard way (input len > 0) but tap configured as TS enabled");
|
||||
test_ptp_msg.ptp_msg.ptp_hdr.sequence_id++;
|
||||
exp_sequence_id++;
|
||||
errno = 0;
|
||||
n = write(eth_tap_fd, &test_ptp_msg, sizeof(test_ptp_msg));
|
||||
TEST_ASSERT_EQUAL(-1, n);
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
errno = 0;
|
||||
n = read(eth_tap_fd, &in_buffer, sizeof(test_ptp_msg));
|
||||
TEST_ASSERT_EQUAL(-1, n);
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
@@ -985,6 +1032,7 @@ TEST_CASE("esp32 l2tap - time stamping", "[ethernet]")
|
||||
ts_info->len = L2TAP_IREC_LEN(sizeof(struct timespec));
|
||||
ptp_msg_ext_buff.buff = NULL;
|
||||
ptp_msg_ext_buff.buff_len = sizeof(test_ptp_msg);
|
||||
errno = 0;
|
||||
n = write(eth_tap_fd, &ptp_msg_ext_buff, 0);
|
||||
TEST_ASSERT_EQUAL(-1, n);
|
||||
TEST_ASSERT_EQUAL(EFAULT, errno);
|
||||
@@ -993,6 +1041,7 @@ TEST_CASE("esp32 l2tap - time stamping", "[ethernet]")
|
||||
ts_info->len = L2TAP_IREC_LEN(1);
|
||||
ptp_msg_ext_buff.buff = NULL;
|
||||
ptp_msg_ext_buff.buff_len = IN_BUFFER_SIZE;
|
||||
errno = 0;
|
||||
n = read(eth_tap_fd, &ptp_msg_ext_buff, 0);
|
||||
TEST_ASSERT_EQUAL(-1, n);
|
||||
TEST_ASSERT_EQUAL(EFAULT, errno);
|
||||
@@ -1050,12 +1099,13 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet]")
|
||||
int eth_tap_fd;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
ESP_LOGI(TAG, "Verify that RCV_FILTER is allowed to be configured only after interface is set...");
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
errno = 0;
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
TEST_ASSERT_EQUAL(EACCES, errno);
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
@@ -1122,6 +1172,7 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet]")
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
ESP_LOGI(TAG, "Verify that the setting the same Ethernet type to other fd at the same interface was unsuccessful...");
|
||||
errno = 0;
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
@@ -1146,7 +1197,7 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet]")
|
||||
int eth_tap_fd;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -1161,6 +1212,7 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet]")
|
||||
// Check getter of direct Ethernet interface handle
|
||||
esp_eth_handle_t l2tap_eth_handle;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_DEVICE_DRV_HNDL, &l2tap_eth_handle));
|
||||
|
||||
TEST_ASSERT_EQUAL(eth_network_hndls.eth_handle, l2tap_eth_handle);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
@@ -1182,6 +1234,7 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet]")
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
|
||||
ESP_LOGI(TAG, "Try to set non-existing Ethernet interface...");
|
||||
errno = 0;
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_NOT_DEF"));
|
||||
TEST_ASSERT_EQUAL(ENODEV, errno);
|
||||
ESP_LOGI(TAG, "Verify that previous setting is kept...");
|
||||
@@ -1235,7 +1288,7 @@ TEST_CASE("esp32 l2tap - ioctl - unknown", "[ethernet]")
|
||||
int eth_tap_fd;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -1260,7 +1313,7 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
|
||||
int eth_tap_fd;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
ethernet_init(ð_network_hndls, true);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
@@ -1302,6 +1355,7 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
|
||||
int loop_cnt = 0;
|
||||
xTaskCreate(send_task, "raw_eth_send_task", 1024, &send_task_ctrl, tskIDLE_PRIORITY + 2, NULL);
|
||||
while (loop_cnt < 100) {
|
||||
errno = 0;
|
||||
if ((n = read(eth_tap_fd, in_buffer, IN_BUFFER_SIZE)) > 0) {
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
break;
|
||||
@@ -1329,6 +1383,7 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
|
||||
loop_cnt = 0;
|
||||
xTaskCreate(send_task, "raw_eth_send_task", 1024, &send_task_ctrl, tskIDLE_PRIORITY + 2, NULL);
|
||||
while (loop_cnt < 100) {
|
||||
errno = 0;
|
||||
if ((n = read(eth_tap_fd, in_buffer, IN_BUFFER_SIZE)) > 0) {
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
break;
|
||||
@@ -1343,14 +1398,17 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
|
||||
TEST_ASSERT_EQUAL(0, loop_cnt);
|
||||
|
||||
// Try to use unsupported operation
|
||||
errno = 0;
|
||||
flags = fcntl(eth_tap_fd, F_DUPFD, 0);
|
||||
TEST_ASSERT_EQUAL(-1, flags);
|
||||
TEST_ASSERT_EQUAL(ENOSYS, errno);
|
||||
|
||||
// Try to set unsupported flag
|
||||
errno = 0;
|
||||
flags = fcntl(eth_tap_fd, F_SETFL, O_TRUNC);
|
||||
TEST_ASSERT_EQUAL(-1, flags);
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
errno = 0;
|
||||
flags = fcntl(eth_tap_fd, F_SETFL, O_TRUNC | O_NONBLOCK);
|
||||
TEST_ASSERT_EQUAL(-1, flags);
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
@@ -1362,6 +1420,52 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
|
||||
ethernet_deinit(ð_network_hndls);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
TEST_CASE("esp32 l2tap - no netif attached", "[ethernet]")
|
||||
{
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
int eth_tap_fd;
|
||||
int n;
|
||||
uint16_t eth_type_filter;
|
||||
esp_eth_handle_t l2tap_eth_handle;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
// No netif attached
|
||||
ethernet_init(ð_network_hndls, false);
|
||||
|
||||
// ==========================================================
|
||||
// Untagged frames, no VLAN child, Ethernet driver handle
|
||||
// ==========================================================
|
||||
ESP_LOGI(TAG, "Verify L2TAP untagged frames with no netif (Ethernet driver handle)...");
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_DEVICE_DRV_HNDL, eth_network_hndls.eth_handle));
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_DEVICE_DRV_HNDL, &l2tap_eth_handle));
|
||||
TEST_ASSERT_EQUAL(eth_network_hndls.eth_handle, l2tap_eth_handle);
|
||||
|
||||
eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &s_test_msg.header.src.addr));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &s_test_msg.header.dest.addr));
|
||||
s_test_msg.header.type = ETH_FILTER_BE;
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(-1, write(eth_tap_fd, &s_test_msg, sizeof(s_test_msg)));
|
||||
n = read(eth_tap_fd, in_buffer, IN_BUFFER_SIZE);
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
ESP_LOGI(TAG, "untagged recv test string: %s", ((test_vfs_eth_tap_msg_t *)in_buffer)->str);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_unregister(NULL));
|
||||
ethernet_deinit(ð_network_hndls);
|
||||
}
|
||||
#if CONFIG_ETH_SUBLAYER_VLAN_SUPPORT
|
||||
// TODO add vlan tests IDF-16041
|
||||
#endif // CONFIG_ETH_SUBLAYER_VLAN_SUPPORT
|
||||
#endif // CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
|
||||
@@ -8,7 +8,9 @@ from pytest_embedded import Dut
|
||||
'config, target',
|
||||
[
|
||||
pytest.param('default_esp32', 'esp32', marks=[pytest.mark.eth_ip101]),
|
||||
pytest.param('sub_esp32', 'esp32', marks=[pytest.mark.eth_ip101]),
|
||||
pytest.param('default_esp32p4', 'esp32p4', marks=[pytest.mark.eth_ip101]),
|
||||
pytest.param('sub_esp32p4', 'esp32p4', marks=[pytest.mark.eth_ip101]),
|
||||
],
|
||||
indirect=['target'],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_ETH_SUBLAYER_SUPPORT=y
|
||||
CONFIG_ETH_TRANSMIT_MUTEX=n
|
||||
CONFIG_ETH_SUBLAYER_TRANSMIT_MUTEX=y
|
||||
CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER=y
|
||||
@@ -0,0 +1,6 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_ETH_SUBLAYER_SUPPORT=y
|
||||
CONFIG_ETH_TRANSMIT_MUTEX=n
|
||||
CONFIG_ETH_SUBLAYER_TRANSMIT_MUTEX=y
|
||||
CONFIG_L2TAP_TEST_USE_ETH_SUBLAYER=y
|
||||
@@ -2,6 +2,7 @@ CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
|
||||
|
||||
CONFIG_ETH_USE_ESP32_EMAC=y
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
CONFIG_ESP_NETIF_L2_TAP=y
|
||||
CONFIG_LWIP_CHECK_THREAD_SAFETY=y
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/param.h>
|
||||
@@ -21,6 +20,7 @@
|
||||
#include "esp_check.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_private/esp_eth_sublayer_iodriver.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
@@ -51,9 +51,18 @@ typedef struct {
|
||||
QueueHandle_t rx_queue;
|
||||
|
||||
SemaphoreHandle_t close_done_sem;
|
||||
|
||||
// Sublayer transmit functions
|
||||
esp_err_t (*iodriver_transmit)(l2tap_iodriver_handle io_handle, void *buf, size_t len);
|
||||
esp_err_t (*iodriver_transmit_wrap)(l2tap_iodriver_handle io_handle, void *buf, size_t len, void *eb);
|
||||
void (*iodriver_free_rx_buffer)(l2tap_iodriver_handle io_handle, void *buffer);
|
||||
esp_err_t (*iodriver_get_ll_driver)(l2tap_iodriver_handle io_handle, void **ll_driver);
|
||||
|
||||
// TODO create JIRA to remove this along with Ethernet glue or update glue to have `get_io_fns`
|
||||
// Direct Ethernet Driver transmit functions (legacy mode)
|
||||
union {
|
||||
esp_err_t (*driver_transmit)(l2tap_iodriver_handle io_handle, void *buffer, size_t len);
|
||||
esp_err_t (*driver_transmit_ctrl_vargs)(l2tap_iodriver_handle io_handle, void *ctrl, uint32_t argc, ...);
|
||||
esp_err_t (*eth_transmit)(l2tap_iodriver_handle io_handle, void *buffer, size_t len);
|
||||
esp_err_t (*eth_transmit_ctrl_bufs)(l2tap_iodriver_handle io_handle, void *ctrl, const esp_eth_buf_desc_t *bufs, size_t buf_count);
|
||||
};
|
||||
void (*driver_free_rx_buffer)(l2tap_iodriver_handle io_handle, void* buffer);
|
||||
} l2tap_context_t;
|
||||
@@ -61,7 +70,8 @@ typedef struct {
|
||||
typedef struct {
|
||||
void *buff;
|
||||
size_t len;
|
||||
eth_mac_time_t ts;
|
||||
void *l2_buff;
|
||||
l2tap_timestamp_t ts;
|
||||
} frame_queue_entry_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -89,11 +99,33 @@ static portMUX_TYPE s_critical_section_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static l2tap_select_args_t **s_registered_selects = NULL;
|
||||
static int32_t s_registered_select_cnt = 0;
|
||||
|
||||
typedef struct l2tap_provider_node {
|
||||
l2tap_iodriver_provider_handle handle;
|
||||
SLIST_ENTRY(l2tap_provider_node) next;
|
||||
} l2tap_provider_node_t;
|
||||
|
||||
static SLIST_HEAD(l2tap_provider_list, l2tap_provider_node) s_provider_list =
|
||||
SLIST_HEAD_INITIALIZER(s_provider_list);
|
||||
|
||||
static const char *TAG = "vfs_l2tap";
|
||||
|
||||
static void l2tap_select_notify(int fd, l2tap_select_notif_e select_notif);
|
||||
|
||||
/* ================== Utils ====================== */
|
||||
static inline void default_free_rx_buffer(l2tap_iodriver_handle io_handle, void* buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void l2tap_free_rx_buffer(l2tap_context_t *l2tap_socket, void *buffer)
|
||||
{
|
||||
if (l2tap_socket->iodriver_free_rx_buffer != NULL) {
|
||||
l2tap_socket->iodriver_free_rx_buffer(l2tap_socket->driver_handle, buffer);
|
||||
return;
|
||||
}
|
||||
l2tap_socket->driver_free_rx_buffer(l2tap_socket->driver_handle, buffer);
|
||||
}
|
||||
|
||||
static esp_err_t init_rx_queue(l2tap_context_t *l2tap_socket)
|
||||
{
|
||||
l2tap_socket->rx_queue = xQueueCreate(RX_QUEUE_MAX_SIZE, sizeof(frame_queue_entry_t));
|
||||
@@ -101,12 +133,13 @@ static esp_err_t init_rx_queue(l2tap_context_t *l2tap_socket)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t push_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t len, eth_mac_time_t *ts)
|
||||
static esp_err_t push_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t len, void *l2_buff, l2tap_timestamp_t *ts)
|
||||
{
|
||||
frame_queue_entry_t rx_frame_info;
|
||||
frame_queue_entry_t rx_frame_info = {0};
|
||||
|
||||
rx_frame_info.buff = buff;
|
||||
rx_frame_info.len = len;
|
||||
rx_frame_info.l2_buff = l2_buff;
|
||||
if (ts) {
|
||||
rx_frame_info.ts = *ts;
|
||||
}
|
||||
@@ -131,7 +164,7 @@ static esp_err_t pop_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t
|
||||
// empty queue was issued indicating the fd is going to be closed
|
||||
if (rx_frame_info.len == 0) {
|
||||
// indicate to "clean_task" that task waiting for queue was unblocked
|
||||
push_rx_queue(l2tap_socket, NULL, 0, NULL);
|
||||
push_rx_queue(l2tap_socket, NULL, 0, NULL, NULL);
|
||||
*copy_len = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -160,8 +193,8 @@ static esp_err_t pop_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t
|
||||
// check if there is enough space to store TS
|
||||
if (info_rec->len - sizeof(l2tap_irec_hdr_t) >= sizeof(struct timespec)) {
|
||||
struct timespec *ts = (struct timespec *)info_rec->data;
|
||||
ts->tv_sec = rx_frame_info.ts.seconds;
|
||||
ts->tv_nsec = rx_frame_info.ts.nanoseconds;
|
||||
ts->tv_sec = rx_frame_info.ts.sec;
|
||||
ts->tv_nsec = rx_frame_info.ts.nsec;
|
||||
} else {
|
||||
info_rec->type = L2TAP_IREC_INVALID;
|
||||
}
|
||||
@@ -176,7 +209,7 @@ static esp_err_t pop_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t
|
||||
}
|
||||
}
|
||||
memcpy(copy_buff, rx_frame_info.buff, *copy_len);
|
||||
l2tap_socket->driver_free_rx_buffer(l2tap_socket->driver_handle, rx_frame_info.buff);
|
||||
l2tap_free_rx_buffer(l2tap_socket, rx_frame_info.l2_buff != NULL ? rx_frame_info.l2_buff : rx_frame_info.buff);
|
||||
} else {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
@@ -193,7 +226,7 @@ static void flush_rx_queue(l2tap_context_t *l2tap_socket)
|
||||
frame_queue_entry_t rx_frame_info;
|
||||
while (xQueueReceive(l2tap_socket->rx_queue, &rx_frame_info, 0) == pdTRUE) {
|
||||
if (rx_frame_info.len > 0) {
|
||||
free(rx_frame_info.buff);
|
||||
l2tap_free_rx_buffer(l2tap_socket, rx_frame_info.l2_buff != NULL ? rx_frame_info.l2_buff : rx_frame_info.buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,13 +247,34 @@ static inline void l2tap_exit_critical(void)
|
||||
portEXIT_CRITICAL(&s_critical_section_lock);
|
||||
}
|
||||
|
||||
static inline void default_free_rx_buffer(l2tap_iodriver_handle io_handle, void* buffer)
|
||||
static esp_err_t l2tap_transmit(l2tap_context_t *l2tap_socket, void *data, size_t size)
|
||||
{
|
||||
free(buffer);
|
||||
if (l2tap_socket->iodriver_transmit != NULL) {
|
||||
return l2tap_socket->iodriver_transmit(l2tap_socket->driver_handle, data, size);
|
||||
}
|
||||
if (l2tap_socket->eth_transmit != NULL) {
|
||||
return l2tap_socket->eth_transmit(l2tap_socket->driver_handle, data, size);
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
static esp_err_t l2tap_transmit_ts(l2tap_context_t *l2tap_socket, void *data, size_t size, l2tap_timestamp_t *hw_ts)
|
||||
{
|
||||
if (l2tap_socket->iodriver_transmit_wrap != NULL) {
|
||||
return l2tap_socket->iodriver_transmit_wrap(l2tap_socket->driver_handle, data, size, hw_ts);
|
||||
}
|
||||
if (l2tap_socket->eth_transmit_ctrl_bufs != NULL) {
|
||||
const esp_eth_buf_desc_t bufs[] = {
|
||||
{ .buf = (uint8_t *)data, .len = size },
|
||||
};
|
||||
return l2tap_socket->eth_transmit_ctrl_bufs(l2tap_socket->driver_handle, hw_ts, bufs, 1);
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
|
||||
/* ================== ESP NETIF L2 TAP intf ====================== */
|
||||
esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, void *buff, size_t *size, void *info)
|
||||
esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, void *buff, size_t *size, l2tap_eth_filter_info_t *info)
|
||||
{
|
||||
struct eth_hdr *eth_header = buff;
|
||||
uint16_t eth_type = ntohs(eth_header->type);
|
||||
@@ -233,15 +287,17 @@ esp_err_t esp_vfs_l2tap_eth_filter_frame(l2tap_iodriver_handle driver_handle, vo
|
||||
// Note that IEEE 802.2 LLC resolution is expected to be performed by upper stream app
|
||||
(s_l2tap_sockets[i].ethtype_filter <= ETH_IEEE802_3_MAX_LEN && eth_type <= ETH_IEEE802_3_MAX_LEN))) {
|
||||
l2tap_exit_critical();
|
||||
eth_mac_time_t *ts;
|
||||
if (s_l2tap_sockets[i].flags & L2TAP_FLAG_TS) {
|
||||
ts = (eth_mac_time_t *)info;
|
||||
} else {
|
||||
ts = NULL;
|
||||
l2tap_timestamp_t *ts = NULL;
|
||||
void *l2_buffer = NULL;
|
||||
if (info != NULL) {
|
||||
if (s_l2tap_sockets[i].flags & L2TAP_FLAG_TS) {
|
||||
ts = info->hw_ts;
|
||||
}
|
||||
l2_buffer = info->l2_buffer;
|
||||
}
|
||||
if (push_rx_queue(&s_l2tap_sockets[i], buff, *size, ts) != ESP_OK) {
|
||||
if (push_rx_queue(&s_l2tap_sockets[i], buff, *size, l2_buffer, ts) != ESP_OK) {
|
||||
// just tail drop when queue is full
|
||||
s_l2tap_sockets[i].driver_free_rx_buffer(s_l2tap_sockets[i].driver_handle, buff);
|
||||
l2tap_free_rx_buffer(&s_l2tap_sockets[i], l2_buffer != NULL ? l2_buffer : buff);
|
||||
ESP_LOGD(TAG, "fd %d rx queue is full", i);
|
||||
}
|
||||
l2tap_enter_critical();
|
||||
@@ -274,8 +330,12 @@ static int l2tap_open(__attribute__((unused)) void *ctx, const char *path, int f
|
||||
s_l2tap_sockets[fd].ethtype_filter = 0x0;
|
||||
s_l2tap_sockets[fd].flags = 0;
|
||||
s_l2tap_sockets[fd].driver_handle = NULL;
|
||||
s_l2tap_sockets[fd].iodriver_transmit = NULL;
|
||||
s_l2tap_sockets[fd].iodriver_transmit_wrap = NULL;
|
||||
s_l2tap_sockets[fd].iodriver_free_rx_buffer = NULL;
|
||||
s_l2tap_sockets[fd].iodriver_get_ll_driver = NULL;
|
||||
s_l2tap_sockets[fd].flags |= ((flags & O_NONBLOCK) == O_NONBLOCK) ? L2TAP_FLAG_NON_BLOCK : 0;
|
||||
s_l2tap_sockets[fd].driver_transmit = esp_eth_transmit;
|
||||
s_l2tap_sockets[fd].eth_transmit = esp_eth_transmit;
|
||||
s_l2tap_sockets[fd].driver_free_rx_buffer = default_free_rx_buffer;
|
||||
atomic_store(&s_l2tap_sockets[fd].state, L2TAP_SOCK_STATE_OPENED);
|
||||
return fd;
|
||||
@@ -349,8 +409,8 @@ static ssize_t l2tap_write(__attribute__((unused)) void *ctx, int fd, const void
|
||||
}
|
||||
|
||||
if (s_l2tap_sockets[fd].flags & L2TAP_FLAG_TS) {
|
||||
eth_mac_time_t eth_ts;
|
||||
if ((esp_ret = s_l2tap_sockets[fd].driver_transmit_ctrl_vargs(s_l2tap_sockets[fd].driver_handle, ð_ts, 2, eth_buff, size)) == ESP_OK){
|
||||
l2tap_timestamp_t hw_ts;
|
||||
if ((esp_ret = l2tap_transmit_ts(&s_l2tap_sockets[fd], eth_buff, size, &hw_ts)) == ESP_OK){
|
||||
// find the record allocated for the time stamp info
|
||||
l2tap_irec_hdr_t *info_rec = L2TAP_IREC_FIRST(ext_buff);
|
||||
while(info_rec != NULL) {
|
||||
@@ -363,18 +423,19 @@ static ssize_t l2tap_write(__attribute__((unused)) void *ctx, int fd, const void
|
||||
if (info_rec != NULL) {
|
||||
if (info_rec->len - sizeof(l2tap_irec_hdr_t) >= sizeof(struct timespec)) {
|
||||
struct timespec *ts = (struct timespec *)info_rec->data;
|
||||
ts->tv_sec = eth_ts.seconds;
|
||||
ts->tv_nsec = eth_ts.nanoseconds;
|
||||
ts->tv_sec = hw_ts.sec;
|
||||
ts->tv_nsec = hw_ts.nsec;
|
||||
} else {
|
||||
info_rec->type = L2TAP_IREC_INVALID;
|
||||
}
|
||||
}
|
||||
ret = size;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "l2tap_transmit_ts failed: %d", esp_ret);
|
||||
errno = l2tap_tx_esp_err_to_errno(esp_ret);
|
||||
}
|
||||
} else {
|
||||
if ((esp_ret = s_l2tap_sockets[fd].driver_transmit(s_l2tap_sockets[fd].driver_handle, eth_buff, size)) == ESP_OK) {
|
||||
if ((esp_ret = l2tap_transmit(&s_l2tap_sockets[fd], eth_buff, size)) == ESP_OK) {
|
||||
ret = size;
|
||||
} else {
|
||||
errno = l2tap_tx_esp_err_to_errno(esp_ret);
|
||||
@@ -449,7 +510,7 @@ static void l2tap_clean_task(void *task_param)
|
||||
flush_rx_queue(l2tap_socket);
|
||||
|
||||
// push empty queue to unblock possibly blocking task
|
||||
push_rx_queue(l2tap_socket, NULL, 0, NULL);
|
||||
push_rx_queue(l2tap_socket, NULL, 0, NULL, NULL);
|
||||
// wait for the indication that blocking task was executed (unblocked)
|
||||
ssize_t actual_size;
|
||||
pop_rx_queue(l2tap_socket, NULL, 0, &actual_size);
|
||||
@@ -500,7 +561,7 @@ static int l2tap_close(__attribute__((unused)) void *ctx, int fd)
|
||||
|
||||
close_failed:
|
||||
flush_rx_queue(&s_l2tap_sockets[fd]);
|
||||
(void)push_rx_queue(&s_l2tap_sockets[fd], NULL, 0, NULL);
|
||||
(void)push_rx_queue(&s_l2tap_sockets[fd], NULL, 0, NULL, NULL);
|
||||
delete_rx_queue(&s_l2tap_sockets[fd]);
|
||||
atomic_store(&s_l2tap_sockets[fd].state, L2TAP_SOCK_STATE_READY);
|
||||
errno = ENOMEM;
|
||||
@@ -513,6 +574,43 @@ static bool netif_driver_matches(esp_netif_t *netif, void* driver)
|
||||
return esp_netif_get_io_driver(netif) == driver;
|
||||
}
|
||||
|
||||
static void l2tap_socket_apply_io_fns(l2tap_context_t *l2tap_socket, const esp_eth_iodriver_io_fns_t *io_fns)
|
||||
{
|
||||
l2tap_socket->driver_handle = io_fns->io_handle;
|
||||
l2tap_socket->iodriver_transmit = io_fns->iodriver_transmit;
|
||||
l2tap_socket->iodriver_transmit_wrap = io_fns->iodriver_transmit_wrap;
|
||||
l2tap_socket->iodriver_free_rx_buffer = io_fns->iodriver_free_rx_buffer;
|
||||
l2tap_socket->iodriver_get_ll_driver = io_fns->iodriver_get_ll_driver;
|
||||
}
|
||||
|
||||
static void l2tap_bind_io_handle(l2tap_context_t *l2tap_socket, l2tap_iodriver_handle io_handle)
|
||||
{
|
||||
l2tap_provider_node_t *it;
|
||||
esp_eth_iodriver_provider_base_t *provider;
|
||||
esp_eth_iodriver_io_fns_t io_fns;
|
||||
|
||||
l2tap_enter_critical();
|
||||
// NULL handle can't be served by any provider, don't even try and bind it directly (legacy mode)
|
||||
if (io_handle != NULL) {
|
||||
SLIST_FOREACH(it, &s_provider_list, next) {
|
||||
provider = (esp_eth_iodriver_provider_base_t *)it->handle;
|
||||
if (provider->get_io_fns != NULL &&
|
||||
provider->get_io_fns(provider, io_handle, &io_fns) == ESP_OK) {
|
||||
l2tap_socket_apply_io_fns(l2tap_socket, &io_fns);
|
||||
l2tap_exit_critical();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No iodriver provider matched: bind the handle directly (legacy mode).
|
||||
l2tap_socket->driver_handle = io_handle;
|
||||
l2tap_socket->iodriver_transmit = NULL;
|
||||
l2tap_socket->iodriver_transmit_wrap = NULL;
|
||||
l2tap_socket->iodriver_free_rx_buffer = NULL;
|
||||
l2tap_socket->iodriver_get_ll_driver = NULL;
|
||||
l2tap_exit_critical();
|
||||
}
|
||||
|
||||
static int l2tap_ioctl(__attribute__((unused)) void *ctx, int fd, int cmd, va_list args)
|
||||
{
|
||||
esp_netif_t *esp_netif;
|
||||
@@ -552,15 +650,21 @@ static int l2tap_ioctl(__attribute__((unused)) void *ctx, int fd, int cmd, va_li
|
||||
}
|
||||
case L2TAP_S_INTF_DEVICE:{
|
||||
const char *str = va_arg(args, const char *);
|
||||
// get netif handle from if key (the highest layer)
|
||||
esp_netif = esp_netif_get_handle_from_ifkey(str);
|
||||
if (esp_netif == NULL) {
|
||||
// No such device
|
||||
errno = ENODEV;
|
||||
goto err;
|
||||
}
|
||||
l2tap_enter_critical();
|
||||
s_l2tap_sockets[fd].driver_handle = esp_netif_get_io_driver(esp_netif);
|
||||
l2tap_exit_critical();
|
||||
// find the iodriver provider based on the netif iodriver
|
||||
esp_netif_iodriver_handle netif_iodriver = esp_netif_get_io_driver(esp_netif);
|
||||
if (netif_iodriver == NULL) {
|
||||
// No such device (netif does not have any IO driver attached)
|
||||
errno = ENODEV;
|
||||
goto err;
|
||||
}
|
||||
l2tap_bind_io_handle(&s_l2tap_sockets[fd], netif_iodriver);
|
||||
break;
|
||||
}
|
||||
case L2TAP_G_INTF_DEVICE:{
|
||||
@@ -572,26 +676,34 @@ static int l2tap_ioctl(__attribute__((unused)) void *ctx, int fd, int cmd, va_li
|
||||
break;
|
||||
}
|
||||
case L2TAP_S_DEVICE_DRV_HNDL:{
|
||||
l2tap_iodriver_handle set_driver_hdl = va_arg(args, l2tap_iodriver_handle);
|
||||
if (set_driver_hdl == NULL) {
|
||||
l2tap_iodriver_handle device_driver_hdl = va_arg(args, l2tap_iodriver_handle);
|
||||
if (device_driver_hdl == NULL) {
|
||||
// No such device (not valid driver handle)
|
||||
errno = ENODEV;
|
||||
errno = EINVAL;
|
||||
goto err;
|
||||
}
|
||||
l2tap_enter_critical();
|
||||
s_l2tap_sockets[fd].driver_handle = set_driver_hdl;
|
||||
l2tap_exit_critical();
|
||||
l2tap_bind_io_handle(&s_l2tap_sockets[fd], device_driver_hdl);
|
||||
break;
|
||||
}
|
||||
case L2TAP_G_DEVICE_DRV_HNDL:{
|
||||
l2tap_iodriver_handle *get_driver_hdl = va_arg(args, l2tap_iodriver_handle*);
|
||||
*get_driver_hdl = s_l2tap_sockets[fd].driver_handle;
|
||||
// the driver handle and the associated getter must be read consistently since they can be
|
||||
// reassigned from other task
|
||||
l2tap_enter_critical();
|
||||
// no iodriver provider registered, get the driver handle directly (legacy mode)
|
||||
if (s_l2tap_sockets[fd].iodriver_get_ll_driver == NULL) {
|
||||
*get_driver_hdl = s_l2tap_sockets[fd].driver_handle;
|
||||
} else {
|
||||
s_l2tap_sockets[fd].iodriver_get_ll_driver(s_l2tap_sockets[fd].driver_handle, get_driver_hdl);
|
||||
}
|
||||
l2tap_exit_critical();
|
||||
break;
|
||||
}
|
||||
case L2TAP_S_TIMESTAMP_EN:
|
||||
l2tap_enter_critical();
|
||||
s_l2tap_sockets[fd].flags |= L2TAP_FLAG_TS;
|
||||
s_l2tap_sockets[fd].driver_transmit_ctrl_vargs = esp_eth_transmit_ctrl_vargs;
|
||||
// TODO add JIRA ticket - maybe not needed tests with sublayer where L2TAP_S_TIMESTAMP_EN may not be needed
|
||||
s_l2tap_sockets[fd].eth_transmit_ctrl_bufs = esp_eth_transmit_ctrl_bufs;
|
||||
l2tap_exit_critical();
|
||||
break;
|
||||
default:
|
||||
@@ -822,6 +934,61 @@ static const esp_vfs_fs_ops_t s_vfs_l2tap = {
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
};
|
||||
|
||||
/* ================== IO Driver Provider Registry ====================== */
|
||||
|
||||
esp_err_t esp_vfs_l2tap_iodriver_provider_register(l2tap_iodriver_provider_handle provider)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(provider, ESP_ERR_INVALID_ARG, TAG, "iodriver provider handle is NULL");
|
||||
|
||||
l2tap_provider_node_t *node = malloc(sizeof(*node));
|
||||
if (!node) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
node->handle = provider;
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
l2tap_enter_critical();
|
||||
l2tap_provider_node_t *it;
|
||||
SLIST_FOREACH(it, &s_provider_list, next) {
|
||||
if (it->handle == provider) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret == ESP_OK) {
|
||||
SLIST_INSERT_HEAD(&s_provider_list, node, next);
|
||||
}
|
||||
l2tap_exit_critical();
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
free(node);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_l2tap_iodriver_provider_unregister(l2tap_iodriver_provider_handle provider)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(provider, ESP_ERR_INVALID_ARG, TAG, "iodriver provider handle is NULL");
|
||||
|
||||
l2tap_provider_node_t *found = NULL;
|
||||
l2tap_enter_critical();
|
||||
l2tap_provider_node_t *it;
|
||||
SLIST_FOREACH(it, &s_provider_list, next) {
|
||||
if (it->handle == provider) {
|
||||
found = it;
|
||||
SLIST_REMOVE(&s_provider_list, it, l2tap_provider_node, next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
l2tap_exit_critical();
|
||||
|
||||
if (found) {
|
||||
free(found);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config)
|
||||
{
|
||||
l2tap_vfs_config_t def_config = L2TAP_VFS_CONFIG_DEFAULT();
|
||||
|
||||
Reference in New Issue
Block a user