mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Introduce the experimental Ethernet sublayer (parent/child VLAN, optional switch path, iodriver provider), restructure eth test apps, and document the new APIs.
361 lines
16 KiB
C
361 lines
16 KiB
C
/*
|
|
* 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
|