mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
feat(esp_eth): add Ethernet sublayer with optional VLAN and switch
Introduce the experimental Ethernet sublayer (parent/child VLAN, optional switch path, iodriver provider), restructure eth test apps, and document the new APIs.
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/**
|
||||
* Purpose of this file is to create a common header for the typical ethernet usecase,
|
||||
* so using the ethernet driver and the default glue layer to its network interface.
|
||||
* so using the ethernet driver and the default glue or sublayer layer to its network interface.
|
||||
*
|
||||
* If you prefer to create a custom network interface or use the Ethernet as a driver only,
|
||||
* then it is recommended to include the "esp_eth_driver.h" only.
|
||||
*/
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_eth_netif_glue.h"
|
||||
#if CONFIG_ETH_SUBLAYER_SUPPORT
|
||||
#include "esp_eth_sublayer.h"
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_event_base.h"
|
||||
#include "hal/eth_types.h"
|
||||
@@ -14,6 +16,14 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Scatter-gather buffer descriptor for extended Ethernet transmit.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buf; /*!< Pointer to the buffer segment */
|
||||
size_t len; /*!< Length of the buffer segment in bytes */
|
||||
} esp_eth_buf_desc_t;
|
||||
|
||||
/**
|
||||
* @brief Offset for start of MAC custom ioctl commands
|
||||
*
|
||||
|
||||
@@ -324,6 +324,8 @@ esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length);
|
||||
/**
|
||||
* @brief Extended Transmit with variable number of arguments
|
||||
*
|
||||
* @deprecated Use esp_eth_transmit_ctrl_bufs() instead.
|
||||
*
|
||||
* @note Typical intended use case of this function is to assemble Ethernet frame from multiple input buffers
|
||||
* at lower layer of the driver (MAC layer) to avoid unnecessary buffer reallocation and copy.
|
||||
*
|
||||
@@ -337,12 +339,13 @@ esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length);
|
||||
* - ESP_ERR_TIMEOUT: transmit frame buffer failed because HW was not get available in predefined period
|
||||
* - ESP_FAIL: transmit frame buffer failed because some other error occurred
|
||||
*/
|
||||
esp_err_t esp_eth_transmit_ctrl_vargs(esp_eth_handle_t hdl, void *ctrl, uint32_t argc, ...);
|
||||
esp_err_t esp_eth_transmit_ctrl_vargs(esp_eth_handle_t hdl, void *ctrl, uint32_t argc, ...)
|
||||
__attribute__((deprecated("Use esp_eth_transmit_ctrl_bufs() instead")));
|
||||
|
||||
/**
|
||||
* @brief Wrapper over Extended Transmit function to ensure backward compatibility.
|
||||
*
|
||||
* @note For new implementations, it is recommended to use `esp_eth_transmit_ctrl_vargs()` directly.
|
||||
* @note For new implementations, it is recommended to use `esp_eth_transmit_ctrl_bufs()` directly.
|
||||
*
|
||||
* @param[in] eth_hdl handle of Ethernet driver
|
||||
* @param[in] argc number variable arguments
|
||||
@@ -355,6 +358,25 @@ esp_err_t esp_eth_transmit_ctrl_vargs(esp_eth_handle_t hdl, void *ctrl, uint32_t
|
||||
*/
|
||||
#define esp_eth_transmit_vargs(eth_hdl, argc, ...) esp_eth_transmit_ctrl_vargs(eth_hdl, NULL, (argc) * 2, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Extended transmit from scatter-gather buffer descriptors.
|
||||
*
|
||||
* @note Typical intended use case is to assemble an Ethernet frame from multiple
|
||||
* input buffers at the MAC layer to avoid unnecessary buffer reallocation
|
||||
* and copy.
|
||||
*
|
||||
* @param hdl handle of Ethernet driver
|
||||
* @param ctrl optional transmit control structure (MAC specific), set to NULL when not required
|
||||
* @param bufs array of buffer descriptors comprising the frame
|
||||
* @param buf_count number of valid entries in @a bufs
|
||||
* @return
|
||||
* - ESP_OK: transmit successful
|
||||
* - ESP_ERR_INVALID_STATE: invalid driver state (e.i., driver is not started)
|
||||
* - ESP_ERR_TIMEOUT: transmit frame buffer failed because HW was not get available in predefined period
|
||||
* - ESP_FAIL: transmit frame buffer failed because some other error occurred
|
||||
*/
|
||||
esp_err_t esp_eth_transmit_ctrl_bufs(esp_eth_handle_t hdl, void *ctrl, const esp_eth_buf_desc_t *bufs, size_t buf_count);
|
||||
|
||||
/**
|
||||
* @brief Misc IO function of Ethernet driver
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -104,9 +104,32 @@ struct esp_eth_mac_s {
|
||||
*/
|
||||
esp_err_t (*transmit)(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Transmit packet with extended control from scatter-gather buffer descriptors.
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] ctrl: optional transmit control structure (chip specific), set to NULL when not required
|
||||
* @param[in] bufs: array of buffer descriptors comprising the frame
|
||||
* @param[in] buf_count: number of valid entries in @a bufs
|
||||
*
|
||||
* @note Typical intended use case is to make possible to construct a frame from multiple higher layer
|
||||
* buffers without a need of buffer reallocations.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: transmit packet successfully
|
||||
* - ESP_ERR_INVALID_SIZE: number of actually sent bytes differs to expected
|
||||
* - ESP_FAIL: transmit packet failed because some other error occurred
|
||||
*
|
||||
* @note Returned error codes may differ for each specific MAC chip.
|
||||
*
|
||||
*/
|
||||
esp_err_t (*transmit_ctrl_bufs)(esp_eth_mac_t *mac, void *ctrl, const esp_eth_buf_desc_t *bufs, size_t buf_count);
|
||||
|
||||
/**
|
||||
* @brief Transmit packet with extended control from Ethernet MAC and constructed with special parameters at Layer2.
|
||||
*
|
||||
* @warning Deprecated, use `transmit_ctrl_bufs()` instead.
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] ctrl: optional transmit control structure (chip specific), set to NULL when not required
|
||||
* @param[in] argc: number variable arguments
|
||||
@@ -123,7 +146,7 @@ struct esp_eth_mac_s {
|
||||
* @note Returned error codes may differ for each specific MAC chip.
|
||||
*
|
||||
*/
|
||||
esp_err_t (*transmit_ctrl_vargs)(esp_eth_mac_t *mac, void *ctrl, uint32_t argc, va_list args);
|
||||
esp_err_t (*transmit_ctrl_vargs)(esp_eth_mac_t *mac, void *ctrl, uint32_t argc, va_list args) __attribute__((deprecated("Use transmit_ctrl_bufs instead")));
|
||||
|
||||
/**
|
||||
* @brief Transmit packet from Ethernet MAC constructed with special parameters at Layer2.
|
||||
@@ -145,7 +168,7 @@ struct esp_eth_mac_s {
|
||||
* @note Returned error codes may differ for each specific MAC chip.
|
||||
*
|
||||
*/
|
||||
esp_err_t (*transmit_vargs)(esp_eth_mac_t *mac, uint32_t argc, va_list args) __attribute__((deprecated("Use transmit_ctrl_vargs instead")));
|
||||
esp_err_t (*transmit_vargs)(esp_eth_mac_t *mac, uint32_t argc, va_list args) __attribute__((deprecated("Use transmit_ctrl_bufs instead")));
|
||||
|
||||
/**
|
||||
* @brief Receive packet from Ethernet MAC
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_ETH_SUBLAYER_SUPPORT
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_eth_sublayer_s *esp_eth_sublayer_handle_t;
|
||||
typedef struct esp_eth_sublayer_vlan_s *esp_eth_sublayer_vlan_handle_t;
|
||||
#if CONFIG_ETH_SUBLAYER_SWITCH_SUPPORT
|
||||
typedef struct esp_eth_sublayer_switch_s *esp_eth_sublayer_switch_handle_t;
|
||||
#endif
|
||||
|
||||
#define ESP_ETH_SUBLAYER_UNTAGGED_VID UINT16_MAX
|
||||
#define ESP_ETH_SUBLAYER_TX_BUF_DESC_CAPACITY CONFIG_ETH_SUBLAYER_TX_BUF_DESC_CAPACITY
|
||||
|
||||
/**
|
||||
* @brief Mutable TX buffer descriptor set passed through the sublayer TX path.
|
||||
*/
|
||||
typedef struct {
|
||||
esp_eth_buf_desc_t *bufs; /*!< Descriptor array */
|
||||
size_t *buf_count; /*!< In/out: number of valid entries in @c bufs */
|
||||
const size_t buf_capacity; /*!< Maximum number of entries in @c bufs */
|
||||
} esp_eth_sublayer_tx_bufs_t;
|
||||
|
||||
/**
|
||||
* @brief Event info structure for sublayer optional events.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
esp_event_base_t base;
|
||||
int32_t event_id;
|
||||
void *event_data;
|
||||
} esp_eth_sublayer_event_t;
|
||||
|
||||
/**
|
||||
* @brief Extra information for RX hook callbacks.
|
||||
*/
|
||||
typedef struct {
|
||||
void *l2_buffer; /*!< Starting address of the original Ethernet driverRX allocation */
|
||||
void *driver_info; /*!< Opaque per-frame metadata from MAC / stack_input_info, may be NULL */
|
||||
} esp_eth_sublayer_rx_info_t;
|
||||
|
||||
/**
|
||||
* @brief Optional TX hook on frames passed to the Ethernet driver (after VLAN tag insert for VLAN netifs).
|
||||
*
|
||||
* When a VLAN tag is inserted the sublayer splits the frame into two
|
||||
* esp_eth_buf_desc_t entries (tagged header + payload) so that no payload
|
||||
* memcpy is needed. For untagged frames a single descriptor is passed.
|
||||
*
|
||||
* The hook may modify the descriptor array in place:
|
||||
* - Change @c buf pointers, @c len values, and descriptor order (e.g. insert a
|
||||
* prefix in @c bufs[0] and move the original frame to @c bufs[1], encrypt into
|
||||
* a new buffer, or merge scattered segments into one contiguous @c bufs[0]).
|
||||
* Use @c post_tx_hook to free any buffers allocated for these edits after the
|
||||
* driver finishes transmitting.
|
||||
* - Adjust *@a tx_bufs->buf_count up or down, but never exceed @a tx_bufs->buf_capacity.
|
||||
* - Must NOT free the input buffers.
|
||||
* - May set *@a tx_bufs->buf_count to 0 to skip driver transmit and @c post_tx_hook
|
||||
* (e.g. the hook handled the frame elsewhere or chose to drop it). The @a tx_bufs
|
||||
* descriptor set remains caller-owned; only transmission is suppressed.
|
||||
*
|
||||
* @param eth Underlying Ethernet driver handle
|
||||
* @param tx_bufs Mutable buffer descriptor set comprising the frame
|
||||
* @param ctx User context from esp_eth_sublayer_config_t::hook_ctx
|
||||
* @return ESP_OK to transmit; other values abort transmit for this packet
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_tx_hook_t)(esp_eth_handle_t eth,
|
||||
esp_eth_sublayer_tx_bufs_t *tx_bufs,
|
||||
void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Optional post-TX hook called after a successful TX hook, to release resources it allocated.
|
||||
*
|
||||
* @warning The Tx buffer descriptors may have different order than on the output of the TX hook.
|
||||
*
|
||||
* Intended for freeing buffers that were allocated by the TX hook.
|
||||
* Called whenever the TX hook completed with @a *tx_bufs->buf_count > 0, regardless of the transmit result
|
||||
* (or of any failure that occurs after the TX hook and before the actual transmit). This guarantees that
|
||||
* buffers allocated by the TX hook are always released.
|
||||
*
|
||||
* Not called when the TX hook sets @a *tx_bufs->buf_count to 0 (transmit suppressed) or returns non-ESP_OK.
|
||||
*
|
||||
* @param eth Underlying Ethernet driver handle
|
||||
* @param tx_bufs Buffer descriptor set that was passed to (or prepared for) the driver
|
||||
* @param ctx User context from esp_eth_sublayer_config_t::hook_ctx
|
||||
*/
|
||||
typedef void (*esp_eth_sublayer_post_tx_hook_t)(esp_eth_handle_t eth,
|
||||
const esp_eth_sublayer_tx_bufs_t *tx_bufs,
|
||||
void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Optional RX hook on frames from the Ethernet driver (before VLAN demux / input path).
|
||||
*
|
||||
* The hook may strip a leading protocol header (e.g. MACsec SecTAG) by advancing @p buf forward and
|
||||
* decrementing @p len, or remove a trailing trailer by decrementing @p len only.
|
||||
* @p buf must remain within the original allocation.
|
||||
*
|
||||
* Set @c *len to 0 to take ownership of the original allocation and stop further sublayer processing.
|
||||
* The hook must then release or forward @c info->alloc_base itself.
|
||||
*
|
||||
* @param eth Underlying Ethernet driver handle (when integrated switch is used, this is per port handle)
|
||||
* @param[in,out] buf in: frame start; out: frame start with hook-specific header/trailer removed
|
||||
* @param[in,out] len frame length; set to 0 to stop further processing
|
||||
* @param info Per-frame RX metadata; never NULL
|
||||
* @param ctx User context from esp_eth_sublayer_config_t::hook_ctx
|
||||
* @return ESP_OK to continue processing; other values cause the sublayer to free the allocation and stop
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_rx_hook_t)(esp_eth_handle_t eth, uint8_t **buf, uint32_t *len, esp_eth_sublayer_rx_info_t *info, void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Sublayer configuration structure.
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
esp_eth_handle_t eth_handle; /*!< Underlying Ethernet driver handle the sublayer is bound to (required) */
|
||||
|
||||
/**
|
||||
* Optional event that must be observed (in addition to the physical link being up) before the sublayer
|
||||
* reports child netifs as connected. Leave @c base NULL (the default) to bring netifs up as soon as
|
||||
* ETHERNET_EVENT_CONNECTED fires, with no extra confirmation needed.
|
||||
*
|
||||
* The order in which this event and ETHERNET_EVENT_CONNECTED are received is not significant, the child
|
||||
* netifs are brought up once both conditions are met. Any subsequent disconnect (ETHERNET_EVENT_DISCONNECTED,
|
||||
* ETHERNET_EVENT_STOP or @c disconnect_trigger_event) invalidates the confirmation, i.e. the event needs to
|
||||
* be posted again to bring the child netifs up.
|
||||
*/
|
||||
esp_eth_sublayer_event_t connect_confirm_event;
|
||||
|
||||
/**
|
||||
* Optional event that, in addition to ETHERNET_EVENT_DISCONNECTED, forces all child netifs down
|
||||
* (e.g. an upper-layer authentication/link-partner event). Leave @c base NULL (the default) to only
|
||||
* react to ETHERNET_EVENT_DISCONNECTED.
|
||||
*/
|
||||
esp_eth_sublayer_event_t disconnect_trigger_event;
|
||||
|
||||
esp_eth_sublayer_tx_hook_t tx_hook; /*!< Optional TX hook, see esp_eth_sublayer_tx_hook_t (NULL to disable) */
|
||||
esp_eth_sublayer_post_tx_hook_t post_tx_hook; /*!< Optional post-TX hook, see esp_eth_sublayer_post_tx_hook_t (NULL to disable) */
|
||||
esp_eth_sublayer_rx_hook_t rx_hook; /*!< Optional RX hook, see esp_eth_sublayer_rx_hook_t (NULL to disable) */
|
||||
void *hook_ctx; /*!< User context passed to tx_hook, post_tx_hook, and rx_hook */
|
||||
|
||||
/**
|
||||
* Timeout (ms) to acquire the CONFIG_ETH_SUBLAYER_TRANSMIT_MUTEX before failing with ESP_ERR_TIMEOUT.
|
||||
* 0 uses the built-in default. Ignored when CONFIG_ETH_SUBLAYER_TRANSMIT_MUTEX is disabled.
|
||||
*/
|
||||
uint32_t transmit_mutex_timeout_ms;
|
||||
} esp_eth_sublayer_config_t;
|
||||
|
||||
#if CONFIG_ETH_SUBLAYER_SWITCH_SUPPORT
|
||||
/**
|
||||
* @brief Integrated switch "tag process init" callback.
|
||||
*
|
||||
* Invoked once by the sublayer when it is created. The driver allocates and returns (through @p ctx) all the
|
||||
* state it needs to mux/demux Tail Tagged traffic between the single Host Ethernet interface and the per-port
|
||||
* Ethernet interfaces. The sublayer owns the returned @p ctx and passes it back to the demux and deinit callbacks.
|
||||
*
|
||||
* @param[in] host_eth_handle Host Ethernet driver handle (the one the sublayer is bound to)
|
||||
* @param[in] port_eth_handles array of per-port Ethernet driver handles
|
||||
* @param[in] ports_count number of entries in @p port_eth_handles
|
||||
* @param[out] ctx location to store the driver's integrated switch context
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_switch_tag_init_t)(esp_eth_handle_t host_eth_handle,
|
||||
esp_eth_handle_t *port_eth_handles,
|
||||
uint32_t ports_count,
|
||||
void **ctx);
|
||||
|
||||
/**
|
||||
* @brief Integrated switch "tag process deinit" callback. Releases the context allocated by the init callback.
|
||||
*
|
||||
* @param[in] ctx driver integrated switch context returned by the init callback
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_switch_tag_deinit_t)(void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Integrated switch RX demux callback.
|
||||
*
|
||||
* Called by the sublayer at the very beginning of the RX path (before RX hook / L2 TAP / VLAN demux). The driver
|
||||
* strips its switch tag and reports the ingress port number through @p src_port. The frame is NOT re-routed by
|
||||
* this callback; the sublayer continues its normal RX pipeline on the now tag-free frame.
|
||||
*
|
||||
* Tag stripping strategies:
|
||||
* - Trailing tag (e.g. KSZ8863 Tail Tag): decrement @p length.
|
||||
* - Leading tag after the MAC header (e.g. DSA, 802.1Q port-VLAN): advance @p buffer forward and decrement
|
||||
* @p length (typically after memmove of the 12-byte MAC header over the tag).
|
||||
*
|
||||
* The callback must NOT free or realloc the buffer; on error the sublayer frees the original allocation.
|
||||
* @p buffer must remain within the original allocation.
|
||||
*
|
||||
* @param[in] ctx driver integrated switch context returned by the init callback
|
||||
* @param[in,out] buffer in: frame start including the switch tag; out: frame start with tag removed
|
||||
* @param[in,out] length frame length including the tag on input, with the tag removed on output
|
||||
* @param[out] src_port ingress port number (callback may ignore if NULL)
|
||||
* @return ESP_OK on success, error code otherwise (frame will be dropped by the sublayer)
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_switch_demux_t)(void *ctx, uint8_t **buffer, uint32_t *length, int32_t *src_port);
|
||||
|
||||
/**
|
||||
* @brief Integrated switch TX mux callback.
|
||||
*
|
||||
* Called by the sublayer during transmit to append switch-specific tagging (e.g. KSZ8863 Tail Tag) to
|
||||
* @a tx_bufs before the frame is sent on the Host Ethernet interface.
|
||||
*
|
||||
* @param[in] ctx driver integrated switch context returned by the init callback
|
||||
* @param[in,out] tx_bufs buffer descriptor set comprising the frame
|
||||
* @param port destination port number, or negative for default switch lookup behaviour
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
typedef esp_err_t (*esp_eth_sublayer_switch_mux_t)(void *ctx, esp_eth_sublayer_tx_bufs_t *tx_bufs, int32_t port);
|
||||
|
||||
/**
|
||||
* @brief Integrated switch configuration.
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
esp_eth_sublayer_switch_tag_init_t tag_process_init; /*!< Called when the switch is added; returns @c ctx */
|
||||
esp_eth_sublayer_switch_tag_deinit_t tag_process_deinit; /*!< Called when the switch is deleted */
|
||||
esp_eth_sublayer_switch_demux_t demux; /*!< Called per RX frame to strip the tag / resolve the ingress port */
|
||||
esp_eth_sublayer_switch_mux_t mux; /*!< Called per TX frame to append the switch tag */
|
||||
esp_eth_handle_t host_eth_handle;
|
||||
esp_eth_handle_t *port_eth_handles;
|
||||
uint32_t ports_count;
|
||||
} esp_eth_sublayer_switch_config_t;
|
||||
#endif // CONFIG_ETH_SUBLAYER_SWITCH_SUPPORT
|
||||
|
||||
/**
|
||||
* @brief Default sublayer configuration.
|
||||
*
|
||||
*/
|
||||
#define ESP_ETH_SUBLAYER_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.eth_handle = NULL, \
|
||||
.connect_confirm_event = { NULL, 0, NULL }, \
|
||||
.disconnect_trigger_event = { NULL, 0, NULL }, \
|
||||
.tx_hook = NULL, \
|
||||
.post_tx_hook = NULL, \
|
||||
.rx_hook = NULL, \
|
||||
.hook_ctx = NULL, \
|
||||
.transmit_mutex_timeout_ms = 0, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a Ethernet sublayer for an Ethernet driver.
|
||||
*
|
||||
* @note The interface base sublayer is required to be created. Derived child
|
||||
* interfaces can be used and attached to netifs (e.g. VLAN interfaces).
|
||||
*
|
||||
* @warning Management of the Ethernet-Netif sublayer is not thread-safe for performance reasons.
|
||||
* Configure it only from a single task/context (for example during initialization).
|
||||
*
|
||||
* @param config Sublayer configuration
|
||||
* @param[out] sublayer Location to store the created sublayer handle
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_new(const esp_eth_sublayer_config_t *config, esp_eth_sublayer_handle_t *sublayer);
|
||||
|
||||
/**
|
||||
* @brief Delete Ethernet sublayer and all child entries (VLANs).
|
||||
*
|
||||
* Caller must detach and destroy esp_netif instances before calling this.
|
||||
*
|
||||
* @param sublayer Sublayer handle
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_del(esp_eth_sublayer_handle_t sublayer);
|
||||
|
||||
/**
|
||||
* @brief Add a VLAN entry to the sublayer (802.1Q VID 0..4095 excluding duplicates).
|
||||
*
|
||||
* Attach the VLAN netif with esp_netif_attach(esp_netif, returned handle).
|
||||
*
|
||||
* @warning Management of the Ethernet-Netif sublayer is not thread-safe for performance reasons.
|
||||
* Configure it only from a single task/context (for example during initialization).
|
||||
*
|
||||
* @param sublayer Sublayer handle
|
||||
* @param vlan_id VLAN ID (12-bit), or ESP_ETH_SUBLAYER_UNTAGGED_VID for the untagged child
|
||||
* @param[out] vlan Location to store the created VLAN handle
|
||||
* @return ESP_OK on success; ESP_ERR_INVALID_STATE if the VLAN ID is already present;
|
||||
* ESP_ERR_NOT_SUPPORTED if @p vlan_id is not ESP_ETH_SUBLAYER_UNTAGGED_VID and
|
||||
* CONFIG_ETH_SUBLAYER_VLAN_SUPPORT is disabled
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_vlan_add(esp_eth_sublayer_handle_t sublayer, uint16_t vlan_id,
|
||||
esp_eth_sublayer_vlan_handle_t *vlan);
|
||||
|
||||
/**
|
||||
* @brief Remove a VLAN entry from the sublayer.
|
||||
*
|
||||
* @param sublayer Sublayer handle
|
||||
* @param vlan VLAN handle returned by esp_eth_sublayer_vlan_add
|
||||
* @return ESP_ERR_NOT_FOUND if not on this sublayer; ESP_OK otherwise
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_vlan_del(esp_eth_sublayer_handle_t sublayer, esp_eth_sublayer_vlan_handle_t vlan);
|
||||
|
||||
#if CONFIG_ETH_SUBLAYER_SWITCH_SUPPORT
|
||||
/**
|
||||
* @brief Add the integrated switch (Tail Tag) processing to the sublayer.
|
||||
*
|
||||
* A sublayer can hold at most one switch instance. The switch is attached to the sublayer similarly to a VLAN
|
||||
* child but it is a single instance rather than a list. The provided @p config callbacks (`tag_process_init`,
|
||||
* `demux`, `tag_process_deinit`) are implemented by the integrated switch driver (e.g. KSZ8863). `tag_process_init`
|
||||
* is invoked here to create the driver's switch context.
|
||||
*
|
||||
* @warning Management of the Ethernet-Netif sublayer is not thread-safe for performance reasons.
|
||||
* Configure it only from a single task/context (for example during initialization).
|
||||
*
|
||||
* @param sublayer Sublayer handle
|
||||
* @param config Integrated switch configuration (its own configuration, not part of the sublayer config)
|
||||
* @param[out] sw Location to store the created switch handle
|
||||
* @return ESP_OK on success; ESP_ERR_INVALID_STATE if a switch is already added
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_switch_add(esp_eth_sublayer_handle_t sublayer,
|
||||
const esp_eth_sublayer_switch_config_t *config,
|
||||
esp_eth_sublayer_switch_handle_t *sw);
|
||||
|
||||
/**
|
||||
* @brief Remove the integrated switch from the sublayer.
|
||||
*
|
||||
* @param sw Switch handle returned by esp_eth_sublayer_switch_add
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_eth_sublayer_switch_del(esp_eth_sublayer_switch_handle_t sw);
|
||||
#endif // CONFIG_ETH_SUBLAYER_SWITCH_SUPPORT
|
||||
|
||||
/**
|
||||
* @brief Get the underlying Ethernet driver handle from the sublayer.
|
||||
*
|
||||
* @param sublayer Sublayer handle
|
||||
* @return Ethernet driver handle
|
||||
*/
|
||||
esp_eth_handle_t esp_eth_sublayer_get_eth_handle(esp_eth_sublayer_handle_t sublayer);
|
||||
|
||||
/**
|
||||
* @brief Get the underlying Ethernet driver handle from the VLAN child of the sublayer.
|
||||
*
|
||||
* @param vlan VLAN handle
|
||||
* @return Ethernet driver handle
|
||||
*/
|
||||
esp_eth_handle_t esp_eth_sublayer_vlan_get_eth_handle(esp_eth_sublayer_vlan_handle_t vlan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_ETH_SUBLAYER_SUPPORT
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* @file esp_private/esp_eth_sublayer_iodriver.h
|
||||
*
|
||||
* @brief Internal (inter-component) IO driver access contract.
|
||||
*
|
||||
* This header is NOT part of the stable public application API. It defines a generic virtual table used
|
||||
* between an IO driver provider (e.g. the Ethernet sublayer) and a consumer (e.g. L2 TAP, or any other
|
||||
* upper layer).
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Opaque handle identifying a single logical IO driver endpoint.
|
||||
*
|
||||
* An IO driver endpoint is a point at which L2 frames can be transmitted and received (e.g. an Ethernet
|
||||
* driver, a VLAN child, or an integrated switch port of the Ethernet sublayer). The concrete meaning of the
|
||||
* handle is defined by the provider that resolves it (see @ref esp_eth_iodriver_provider_base_t).
|
||||
*/
|
||||
typedef void *esp_eth_iodriver_handle;
|
||||
|
||||
/**
|
||||
* @brief Opaque handle identifying an IO driver provider.
|
||||
*
|
||||
* A provider knows how to resolve one or more @ref esp_eth_iodriver_handle values into a set of IO functions
|
||||
* (@ref esp_eth_iodriver_io_fns_t). The Ethernet sublayer is one such provider.
|
||||
*/
|
||||
typedef void *esp_eth_iodriver_provider_handle;
|
||||
|
||||
/**
|
||||
* @brief Set of IO functions for a single IO driver endpoint.
|
||||
*
|
||||
* This is a lightweight virtual table that decouples a consumer (e.g. L2 TAP, or any other upper layer) from
|
||||
* the concrete implementation of an IO driver endpoint. A provider fills this structure for a given
|
||||
* @ref esp_eth_iodriver_handle; the consumer then calls the function pointers passing back @c io_handle.
|
||||
*
|
||||
* @note This concept is intentionally independent of any particular consumer so it can be reused by layers
|
||||
* other than L2 TAP.
|
||||
*/
|
||||
typedef struct {
|
||||
esp_eth_iodriver_handle io_handle; /*!< Handle passed back to the function pointers below */
|
||||
esp_err_t (*iodriver_transmit)(esp_eth_iodriver_handle io_handle, void *buf, size_t len); /*!< Transmit a single buffer */
|
||||
esp_err_t (*iodriver_transmit_wrap)(esp_eth_iodriver_handle io_handle, void *buf, size_t len, void *eb); /*!< Transmit with a driver control/extended buffer (e.g. metadata) */
|
||||
void (*iodriver_free_rx_buffer)(esp_eth_iodriver_handle io_handle, void *buffer); /*!< Free an RX buffer previously received on this endpoint */
|
||||
esp_err_t (*iodriver_get_ll_driver)(esp_eth_iodriver_handle io_handle, void **ll_driver); /*!< Get the underlying low-level driver handle */
|
||||
} esp_eth_iodriver_io_fns_t;
|
||||
|
||||
/**
|
||||
* @brief Base interface implemented by an IO driver provider.
|
||||
*
|
||||
* A provider is registered with a consumer (e.g. L2 TAP) by embedding this structure as the first member of
|
||||
* the provider object, so the provider handle can be cast to @c esp_eth_iodriver_provider_base_t *.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Resolve an IO driver handle into its set of IO functions.
|
||||
*
|
||||
* @warning The consumer may call this callback with its internal lock held. The implementation
|
||||
* must therefore be a simple lookup only: it must not log, allocate, block, or take any lock.
|
||||
*
|
||||
* @param[in] provider_handle Provider handle (the object embedding this base).
|
||||
* @param[in] io_handle IO driver handle to resolve.
|
||||
* @param[out] io_fns Filled with the IO functions for @p io_handle on success.
|
||||
* @return
|
||||
* - ESP_OK if @p io_handle is served by this provider and @p io_fns was filled
|
||||
* - ESP_ERR_NOT_FOUND if this provider does not serve @p io_handle
|
||||
* - ESP_ERR_INVALID_ARG on invalid arguments
|
||||
*/
|
||||
esp_err_t (*get_io_fns)(esp_eth_iodriver_provider_handle provider_handle, void *io_handle, esp_eth_iodriver_io_fns_t *io_fns);
|
||||
} esp_eth_iodriver_provider_base_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user