esp_netif: added support for LwIP bridge

examples: created bridge example
This commit is contained in:
Ondrej Kosta 2022-05-06 16:09:24 +02:00
parent 7f884dc966
commit 53082a22f7
23 changed files with 1362 additions and 23 deletions

View File

@ -37,12 +37,17 @@ list(APPEND srcs
"vfs_l2tap/esp_vfs_l2tap.c")
endif()
if(CONFIG_ESP_NETIF_BRIDGE_EN)
list(APPEND srcs
"lwip/esp_netif_br_glue.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
REQUIRES lwip)
if(CONFIG_ESP_NETIF_L2_TAP)
idf_component_optional_requires(PRIVATE esp_eth)
if(CONFIG_ESP_NETIF_L2_TAP OR CONFIG_ESP_NETIF_BRIDGE_EN)
idf_component_optional_requires(PRIVATE esp_eth)
endif()

View File

@ -56,4 +56,12 @@ menu "ESP NETIF Adapter"
Maximum number of frames queued in opened File descriptor. Once the queue is full, the newly arriving
frames are dropped until the queue has enough room to accept incoming traffic (Tail Drop queue
management).
config ESP_NETIF_BRIDGE_EN
depends on ESP_NETIF_TCPIP_LWIP
bool "Enable LwIP IEEE 802.1D bridge"
default n
help
Enable LwIP IEEE 802.1D bridge support in ESP-NETIF. Note that "Number of clients store data in netif"
(LWIP_NUM_NETIF_CLIENT_DATA) option needs to be properly configured to be LwIP bridge avaiable!
endmenu

View File

@ -276,6 +276,37 @@ void esp_netif_action_remove_ip6_address(void *esp_netif, esp_event_base_t base,
*/
esp_err_t esp_netif_set_default_netif(esp_netif_t *esp_netif);
#if CONFIG_ESP_NETIF_BRIDGE_EN
/**
* @brief Add a port to the bridge
*
* @param esp_netif_br Handle to bridge esp-netif instance
* @param esp_netif_port Handle to port esp-netif instance
* @return ESP_OK on success
*/
esp_err_t esp_netif_bridge_add_port(esp_netif_t *esp_netif_br, esp_netif_t *esp_netif_port);
/**
* @brief Add a static entry to bridge forwarding database
*
* @param esp_netif_br Handle to bridge esp-netif instance
* @param addr MAC address entry to be added
* @param ports_mask Port(s) mask where to be the address forwarded
* @return ESP_OK on success
*/
esp_err_t esp_netif_bridge_fdb_add(esp_netif_t *esp_netif_br, uint8_t *addr, uint64_t ports_mask);
/**
* @brief Remove a static entry from bridge forwarding database
*
* @param esp_netif_br Handle to bridge esp-netif instance
* @param addr MAC address entry to be removed
* @return ESP_OK on success
*/
esp_err_t esp_netif_bridge_fdb_remove(esp_netif_t *esp_netif_br, uint8_t *addr);
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
/**
* @}
*/

View File

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_netif.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Handle of bridge netif glue - an intermediate layer between ESP-NETIF and bridge ports ESP-NETIFs
* to access ports io drivers properties
*
*/
typedef struct esp_netif_br_glue_t* esp_netif_br_glue_handle_t;
/**
* @brief Create a netif glue for bridge
* @note bridge netif glue is used to attach ports netifs to the bridge (e.g. to get io driver statuses)
*
* @return - glue object on success
* - NULL on fail
*/
esp_netif_br_glue_handle_t esp_netif_br_glue_new(void);
/**
* @brief Add a port to the bridge netif glue
*
* @param netif_br_glue bridge netif glue
* @param esp_netif_port port netif
* @return - ESP_OK on success
*/
esp_err_t esp_netif_br_glue_add_port(esp_netif_br_glue_handle_t netif_br_glue, esp_netif_t *esp_netif_port);
/**
* @brief Delete netif glue of bridge
*
* @param netif_br_glue bridge netif glue
* @return - ESP_OK: delete netif glue successfully
*/
esp_err_t esp_netif_br_glue_del(esp_netif_br_glue_handle_t netif_br_glue);
#ifdef __cplusplus
}
#endif

View File

@ -26,7 +26,8 @@ extern "C" {
.lost_ip_event = IP_EVENT_STA_LOST_IP, \
.if_key = "WIFI_STA_DEF", \
.if_desc = "sta", \
.route_prio = 100 \
.route_prio = 100, \
.bridge_info = NULL \
} \
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
@ -39,7 +40,8 @@ extern "C" {
.lost_ip_event = 0, \
.if_key = "WIFI_AP_DEF", \
.if_desc = "ap", \
.route_prio = 10 \
.route_prio = 10, \
.bridge_info = NULL \
};
#endif
@ -52,7 +54,8 @@ extern "C" {
.lost_ip_event = IP_EVENT_ETH_LOST_IP, \
.if_key = "ETH_DEF", \
.if_desc = "eth", \
.route_prio = 50 \
.route_prio = 50, \
.bridge_info = NULL \
};
#define ESP_NETIF_INHERENT_DEFAULT_PPP() \
@ -64,7 +67,8 @@ extern "C" {
.lost_ip_event = IP_EVENT_PPP_LOST_IP, \
.if_key = "PPP_DEF", \
.if_desc = "ppp", \
.route_prio = 20 \
.route_prio = 20, \
.bridge_info = NULL \
};
#define ESP_NETIF_INHERENT_DEFAULT_OPENTHREAD() \
@ -76,7 +80,8 @@ extern "C" {
.lost_ip_event = 0, \
.if_key = "OT_DEF", \
.if_desc = "openthread", \
.route_prio = 15 \
.route_prio = 15, \
.bridge_info = NULL \
};
#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \
@ -88,9 +93,22 @@ extern "C" {
.lost_ip_event = 0, \
.if_key = "SLP_DEF", \
.if_desc = "slip", \
.route_prio = 16 \
.route_prio = 16, \
.bridge_info = NULL \
};
#define ESP_NETIF_INHERENT_DEFAULT_BR() \
{ \
.flags = (esp_netif_flags_t)(ESP_NETIF_DHCP_CLIENT | ESP_NETIF_FLAG_GARP | ESP_NETIF_FLAG_EVENT_IP_MODIFIED | ESP_NETIF_FLAG_IS_BRIDGE), \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \
.get_ip_event = IP_EVENT_ETH_GOT_IP, \
.lost_ip_event = IP_EVENT_ETH_LOST_IP, \
.if_key = "BR", \
.if_desc = "br", \
.route_prio = 70, \
.bridge_info = NULL \
};
/**
* @brief Default configuration reference of ethernet interface
@ -174,6 +192,7 @@ extern "C" {
#define ESP_NETIF_NETSTACK_DEFAULT_ETH _g_esp_netif_netstack_default_eth
#define ESP_NETIF_NETSTACK_DEFAULT_BR _g_esp_netif_netstack_default_br
#define ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA _g_esp_netif_netstack_default_wifi_sta
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
#define ESP_NETIF_NETSTACK_DEFAULT_WIFI_AP _g_esp_netif_netstack_default_wifi_ap
@ -189,6 +208,7 @@ extern "C" {
// - Here referenced only as opaque pointers
//
extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_eth;
extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_br;
extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_sta;
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
extern const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_ap;

View File

@ -28,12 +28,18 @@ extern "C" {
#define ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED ESP_ERR_ESP_NETIF_BASE + 0x05
#define ESP_ERR_ESP_NETIF_NO_MEM ESP_ERR_ESP_NETIF_BASE + 0x06
#define ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED ESP_ERR_ESP_NETIF_BASE + 0x07
#define ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED ESP_ERR_ESP_NETIF_BASE + 0x08
#define ESP_ERR_ESP_NETIF_DRIVER_ATTACH_FAILED ESP_ERR_ESP_NETIF_BASE + 0x08
#define ESP_ERR_ESP_NETIF_INIT_FAILED ESP_ERR_ESP_NETIF_BASE + 0x09
#define ESP_ERR_ESP_NETIF_DNS_NOT_CONFIGURED ESP_ERR_ESP_NETIF_BASE + 0x0A
#define ESP_ERR_ESP_NETIF_MLD6_FAILED ESP_ERR_ESP_NETIF_BASE + 0x0B
#define ESP_ERR_ESP_NETIF_IP6_ADDR_FAILED ESP_ERR_ESP_NETIF_BASE + 0x0C
/**
* @brief Definition of ESP-NETIF bridge controll
*/
#define ESP_NETIF_BR_FLOOD -1
#define ESP_NETIF_BR_DROP 0
#define ESP_NETIF_BR_FDW_CPU (1ULL << 63)
/** @brief Type of esp_netif_object server */
struct esp_netif_obj;
@ -146,8 +152,6 @@ typedef struct {
} ip_event_ap_staipassigned_t;
typedef enum esp_netif_flags {
ESP_NETIF_DHCP_CLIENT = 1 << 0,
ESP_NETIF_DHCP_SERVER = 1 << 1,
@ -156,6 +160,7 @@ typedef enum esp_netif_flags {
ESP_NETIF_FLAG_EVENT_IP_MODIFIED = 1 << 4,
ESP_NETIF_FLAG_IS_PPP = 1 << 5,
ESP_NETIF_FLAG_IS_SLIP = 1 << 6,
ESP_NETIF_FLAG_IS_BRIDGE = 1 << 7,
} esp_netif_flags_t;
typedef enum esp_netif_ip_event_type {
@ -164,6 +169,13 @@ typedef enum esp_netif_ip_event_type {
} esp_netif_ip_event_type_t;
/** LwIP bridge configuration */
typedef struct bridgeif_config {
uint16_t max_fdb_dyn_entries; /*!< maximum number of entries in dynamic forwarding database */
uint16_t max_fdb_sta_entries; /*!< maximum number of entries in static forwarding database */
uint8_t max_ports; /*!< maximum number of ports the bridge can consist of */
} bridgeif_config_t;
//
// ESP-NETIF interface configuration:
// 1) general (behavioral) config (esp_netif_config_t)
@ -187,6 +199,7 @@ typedef struct esp_netif_inherent_config {
routing if (if other netifs are up).
A higher value of route_prio indicates
a higher priority */
bridgeif_config_t *bridge_info; /*!< LwIP bridge configuration */
} esp_netif_inherent_config_t;
typedef struct esp_netif_config esp_netif_config_t;

View File

@ -0,0 +1,250 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "esp_netif_br_glue.h"
#include "esp_eth_driver.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_check.h"
#if CONFIG_ESP_NETIF_BRIDGE_EN
const static char *TAG = "esp_netif_br_glue";
typedef struct esp_netif_br_glue_t esp_netif_br_glue_t;
struct esp_netif_br_glue_t {
esp_netif_driver_base_t base;
bool br_started;
esp_netif_t **ports_esp_netifs;
uint8_t port_cnt;
esp_event_handler_instance_t eth_start_ctx_handler;
esp_event_handler_instance_t eth_stop_ctx_handler;
esp_event_handler_instance_t eth_connect_ctx_handler;
esp_event_handler_instance_t eth_disconnect_ctx_handler;
esp_event_handler_instance_t get_ip_ctx_handler;
};
static esp_err_t esp_eth_post_attach_br(esp_netif_t *esp_netif, void *args)
{
uint8_t eth_mac[6];
esp_netif_br_glue_t *netif_glue = (esp_netif_br_glue_t *)args;
netif_glue->base.netif = esp_netif;
esp_netif_get_mac(esp_netif, eth_mac);
ESP_LOGI(TAG, "%02x:%02x:%02x:%02x:%02x:%02x", eth_mac[0], eth_mac[1],
eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
ESP_LOGI(TAG, "bridge netif glue attached");
return ESP_OK;
}
static void eth_action_start(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
esp_netif_br_glue_t *netif_glue = handler_args;
ESP_LOGD(TAG, "eth_action_start: %p, %p, %d, %p, %p", netif_glue, base, event_id, event_data, *(esp_eth_handle_t *)event_data);
for (int i = 0; i < netif_glue->port_cnt; i++) {
if (eth_handle == esp_netif_get_io_driver(netif_glue->ports_esp_netifs[i])) {
if (netif_glue->br_started == false) {
esp_netif_action_start(netif_glue->base.netif, base, event_id, event_data); // basically creates lwip_netif br instance
netif_glue->br_started = true;
ESP_LOGD(TAG, "bridge netif %p is started", netif_glue->base.netif);
}
esp_netif_bridge_add_port(netif_glue->base.netif, netif_glue->ports_esp_netifs[i]);
}
}
}
static void eth_action_stop(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
esp_netif_br_glue_t *netif_glue = handler_args;
ESP_LOGD(TAG, "eth_action_stop: %p, %p, %d, %p, %p", netif_glue, base, event_id, event_data, *(esp_eth_handle_t *)event_data);
for (int i = 0; i < netif_glue->port_cnt; i++) {
// if one of the bridge's ports is stopped, we need to stop the bridge too, since port's lwip_netif is removed and so it would become
// an invalid reference in the bridge's internal structure (there is no way how to remove single port from bridge in current LwIP)
if (eth_handle == esp_netif_get_io_driver(netif_glue->ports_esp_netifs[i])) {
if (netif_glue->br_started == true) {
esp_netif_action_stop(netif_glue->base.netif, base, event_id, event_data); // basically removes lwip_netif br
netif_glue->br_started = false;
ESP_LOGD(TAG, "bridge netif %p is stopped", netif_glue->base.netif);
}
}
}
}
static void eth_action_connected(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
esp_netif_br_glue_t *netif_glue = handler_args;
ESP_LOGD(TAG, "eth_action_connected: %p, %p, %d, %p, %p", netif_glue, base, event_id, event_data, *(esp_eth_handle_t *)event_data);
// if bridge interface is already up, do nothing
if (esp_netif_is_netif_up(netif_glue->base.netif) == true) {
return;
}
for (int i = 0; i < netif_glue->port_cnt; i++) {
if (eth_handle == esp_netif_get_io_driver(netif_glue->ports_esp_netifs[i])) {
esp_netif_action_connected(netif_glue->base.netif, base, event_id, event_data);
ESP_LOGD(TAG, "bridge netif %p is connected", netif_glue->base.netif);
break;
}
}
}
static void eth_action_disconnected(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
esp_netif_br_glue_t *netif_glue = handler_args;
ESP_LOGD(TAG, "eth_action_disconnected: %p, %p, %d, %p, %p", netif_glue, base, event_id, event_data, *(esp_eth_handle_t *)event_data);
for (int i = 0; i < netif_glue->port_cnt; i++) {
// if this is a Ethernet interface associated with bridge, check if other ports are disconnected
if (eth_handle == esp_netif_get_io_driver(netif_glue->ports_esp_netifs[i])) {
int disc_cnt;
for (disc_cnt = 0; disc_cnt < netif_glue->port_cnt; disc_cnt++) {
if (esp_netif_is_netif_up(netif_glue->ports_esp_netifs[disc_cnt]) == true) {
break;
}
}
// if all ports are disconnected, set bridge as disconnected too
if (disc_cnt >= netif_glue->port_cnt) {
esp_netif_action_disconnected(netif_glue->base.netif, base, event_id, event_data);
ESP_LOGD(TAG, "bridge netif %p is disconnected", netif_glue->base.netif);
}
}
}
}
static void br_action_got_ip(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ip_event_got_ip_t *ip_event = (ip_event_got_ip_t *)event_data;
esp_netif_br_glue_t *netif_glue = handler_args;
ESP_LOGD(TAG, "br_action_got_ip: %p, %p, %d, %p, %p", netif_glue, base, event_id, event_data, *(esp_eth_handle_t *)event_data);
if (netif_glue->base.netif == ip_event->esp_netif) {
esp_netif_action_got_ip(ip_event->esp_netif, base, event_id, event_data);
}
}
static esp_err_t esp_netif_br_glue_clear_instance_handlers(esp_netif_br_glue_handle_t esp_netif_br_glue)
{
ESP_RETURN_ON_FALSE(esp_netif_br_glue, ESP_ERR_INVALID_ARG, TAG, "esp_netif_br_glue handle can't be null");
if (esp_netif_br_glue->eth_start_ctx_handler) {
esp_event_handler_instance_unregister(ETH_EVENT, ETHERNET_EVENT_START, esp_netif_br_glue->eth_start_ctx_handler);
esp_netif_br_glue->eth_start_ctx_handler = NULL;
}
if (esp_netif_br_glue->eth_stop_ctx_handler) {
esp_event_handler_instance_unregister(ETH_EVENT, ETHERNET_EVENT_STOP, esp_netif_br_glue->eth_stop_ctx_handler);
esp_netif_br_glue->eth_stop_ctx_handler = NULL;
}
if (esp_netif_br_glue->eth_connect_ctx_handler) {
esp_event_handler_instance_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, esp_netif_br_glue->eth_connect_ctx_handler);
esp_netif_br_glue->eth_connect_ctx_handler = NULL;
}
if (esp_netif_br_glue->eth_disconnect_ctx_handler) {
esp_event_handler_instance_unregister(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, esp_netif_br_glue->eth_disconnect_ctx_handler);
esp_netif_br_glue->eth_disconnect_ctx_handler = NULL;
}
if (esp_netif_br_glue->get_ip_ctx_handler) {
esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, esp_netif_br_glue->get_ip_ctx_handler);
esp_netif_br_glue->get_ip_ctx_handler = NULL;
}
return ESP_OK;
}
static esp_err_t esp_netif_br_glue_set_instance_handlers(esp_netif_br_glue_handle_t esp_netif_br_glue)
{
ESP_RETURN_ON_FALSE(esp_netif_br_glue, ESP_ERR_INVALID_ARG, TAG, "esp_netif_br_glue handle can't be null");
esp_err_t ret = esp_event_handler_instance_register(ETH_EVENT, ETHERNET_EVENT_START, eth_action_start, esp_netif_br_glue, &esp_netif_br_glue->eth_start_ctx_handler);
if (ret != ESP_OK) {
goto fail;
}
ret = esp_event_handler_instance_register(ETH_EVENT, ETHERNET_EVENT_STOP, eth_action_stop, esp_netif_br_glue, &esp_netif_br_glue->eth_stop_ctx_handler);
if (ret != ESP_OK) {
goto fail;
}
ret = esp_event_handler_instance_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, eth_action_connected, esp_netif_br_glue, &esp_netif_br_glue->eth_connect_ctx_handler);
if (ret != ESP_OK) {
goto fail;
}
ret = esp_event_handler_instance_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, eth_action_disconnected, esp_netif_br_glue, &esp_netif_br_glue->eth_disconnect_ctx_handler);
if (ret != ESP_OK) {
goto fail;
}
ret = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, br_action_got_ip, esp_netif_br_glue, &esp_netif_br_glue->get_ip_ctx_handler);
if (ret != ESP_OK) {
goto fail;
}
return ESP_OK;
fail:
esp_netif_br_glue_clear_instance_handlers(esp_netif_br_glue);
return ret;
}
esp_netif_br_glue_handle_t esp_netif_br_glue_new(void)
{
esp_netif_br_glue_t *netif_glue = calloc(1, sizeof(esp_netif_br_glue_t));
if (!netif_glue) {
ESP_LOGE(TAG, "create netif glue failed");
return NULL;
}
netif_glue->base.post_attach = esp_eth_post_attach_br;
if (esp_netif_br_glue_set_instance_handlers(netif_glue) != ESP_OK) {
esp_netif_br_glue_del(netif_glue);
return NULL;
}
return netif_glue;
}
esp_err_t esp_netif_br_glue_add_port(esp_netif_br_glue_handle_t netif_br_glue, esp_netif_t *esp_netif_port)
{
if (netif_br_glue->ports_esp_netifs == NULL) {
netif_br_glue->ports_esp_netifs = malloc(sizeof(esp_netif_t *));
} else {
netif_br_glue->ports_esp_netifs = realloc(netif_br_glue->ports_esp_netifs, (netif_br_glue->port_cnt + 1) * sizeof(esp_netif_t *));
}
if (!netif_br_glue->ports_esp_netifs) {
ESP_LOGE(TAG, "no memory to add br port");
return ESP_ERR_NO_MEM;
}
netif_br_glue->ports_esp_netifs[netif_br_glue->port_cnt] = esp_netif_port;
netif_br_glue->port_cnt++;
return ESP_OK;
}
esp_err_t esp_netif_br_glue_del(esp_netif_br_glue_handle_t netif_br_glue)
{
esp_netif_br_glue_clear_instance_handlers(netif_br_glue);
free(netif_br_glue->ports_esp_netifs);
free(netif_br_glue);
netif_br_glue = NULL;
return ESP_OK;
}
#endif // CONFIG_ESP_NETIF_BRIDGE_EN

View File

@ -26,13 +26,16 @@
#include "lwip/priv/tcpip_priv.h"
#include "lwip/netif.h"
#include "lwip/etharp.h"
#if CONFIG_ESP_NETIF_BRIDGE_EN
#include "netif/bridgeif.h"
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
#include "lwip/dns.h"
#endif
#endif // LWIP_DNS
#if CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT
#include "lwip_default_hooks.h"
#endif
#endif // CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT
#include "esp_netif_lwip_ppp.h"
#include "esp_netif_lwip_slip.h"
@ -340,6 +343,39 @@ esp_err_t esp_netif_set_default_netif(esp_netif_t *esp_netif)
return esp_netif_update_default_netif(esp_netif, ESP_NETIF_SET_DEFAULT);
}
#if CONFIG_ESP_NETIF_BRIDGE_EN
esp_err_t esp_netif_bridge_add_port(esp_netif_t *esp_netif_br, esp_netif_t *esp_netif_port)
{
if (ERR_OK != bridgeif_add_port(esp_netif_br->lwip_netif, esp_netif_port->lwip_netif)) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_netif_bridge_fdb_add(esp_netif_t *esp_netif_br, uint8_t *addr, uint64_t ports_mask)
{
bridgeif_portmask_t ports;
if (ports_mask == ESP_NETIF_BR_FDW_CPU) {
ports = 1 << BRIDGEIF_MAX_PORTS;
} else {
ports = (bridgeif_portmask_t)ports_mask;
}
if (ERR_OK != bridgeif_fdb_add(esp_netif_br->lwip_netif, (const struct eth_addr *)addr, ports)) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_netif_bridge_fdb_remove(esp_netif_t *esp_netif_br, uint8_t *addr)
{
if (ERR_OK != bridgeif_fdb_remove(esp_netif_br->lwip_netif, (const struct eth_addr *)addr)) {
return ESP_FAIL;
}
return ESP_OK;
}
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
void esp_netif_set_ip4_addr(esp_ip4_addr_t *addr, uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{
ip4_addr_t *address = (ip4_addr_t*)addr;
@ -383,7 +419,23 @@ esp_netif_t* esp_netif_get_handle_from_netif_impl(void *dev)
{
// ppp_pcb ptr would never get to app code, so this function only works with vanilla lwip impl
struct netif *lwip_netif = dev;
#if CONFIG_ESP_NETIF_BRIDGE_EN
// bridge lwip netif uses "state" member for something different => need to traverse all esp_netifs
if (lwip_netif->name[0] == 'b' && lwip_netif->name[1] == 'r') {
esp_netif_t* esp_netif = esp_netif_next(NULL);
do
{
if(esp_netif->lwip_netif == lwip_netif) {
return esp_netif;
}
} while ((esp_netif = esp_netif_next(esp_netif)) != NULL);
} else {
return lwip_netif->state;
}
return NULL;
#else
return lwip_netif->state;
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
}
void* esp_netif_get_netif_impl(esp_netif_t *esp_netif)
@ -485,6 +537,19 @@ static esp_err_t esp_netif_init_configuration(esp_netif_t *esp_netif, const esp_
esp_netif->route_prio = cfg->base->route_prio;
}
#if CONFIG_ESP_NETIF_BRIDGE_EN
// Setup bridge configuration if the interface is to be bridge
if (cfg->base->flags & ESP_NETIF_FLAG_IS_BRIDGE) {
if (cfg->base->bridge_info != NULL) {
esp_netif->max_fdb_dyn_entries = cfg->base->bridge_info->max_fdb_dyn_entries;
esp_netif->max_fdb_sta_entries = cfg->base->bridge_info->max_fdb_sta_entries;
esp_netif->max_ports = cfg->base->bridge_info->max_ports;
} else {
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
}
}
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
// Install network stack functions -- connects netif and L3 stack
const esp_netif_netstack_config_t *esp_netif_stack_config = cfg->stack;
if (cfg->base->flags & ESP_NETIF_FLAG_IS_PPP) {
@ -668,12 +733,31 @@ static esp_err_t esp_netif_lwip_add(esp_netif_t *esp_netif)
#endif
}
if (NULL == netif_add(esp_netif->lwip_netif, (struct ip4_addr*)&esp_netif->ip_info->ip,
(struct ip4_addr*)&esp_netif->ip_info->netmask, (struct ip4_addr*)&esp_netif->ip_info->gw,
esp_netif, esp_netif->lwip_init_fn, tcpip_input)) {
esp_netif_lwip_remove(esp_netif);
return ESP_ERR_ESP_NETIF_IF_NOT_READY;
#if CONFIG_ESP_NETIF_BRIDGE_EN
if (esp_netif->flags & ESP_NETIF_FLAG_IS_BRIDGE) {
bridgeif_initdata_t bridge_initdata = {
.max_fdb_dynamic_entries = esp_netif->max_fdb_dyn_entries,
.max_fdb_static_entries = esp_netif->max_fdb_sta_entries,
.max_ports = esp_netif->max_ports
};
memcpy(&bridge_initdata.ethaddr, esp_netif->mac, ETH_HWADDR_LEN);
if (NULL == netif_add(esp_netif->lwip_netif, (struct ip4_addr*)&esp_netif->ip_info->ip,
(struct ip4_addr*)&esp_netif->ip_info->netmask, (struct ip4_addr*)&esp_netif->ip_info->gw,
&bridge_initdata, esp_netif->lwip_init_fn, tcpip_input)) {
esp_netif_lwip_remove(esp_netif);
return ESP_ERR_ESP_NETIF_IF_NOT_READY;
}
} else {
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
if (NULL == netif_add(esp_netif->lwip_netif, (struct ip4_addr*)&esp_netif->ip_info->ip,
(struct ip4_addr*)&esp_netif->ip_info->netmask, (struct ip4_addr*)&esp_netif->ip_info->gw,
esp_netif, esp_netif->lwip_init_fn, tcpip_input)) {
esp_netif_lwip_remove(esp_netif);
return ESP_ERR_ESP_NETIF_IF_NOT_READY;
}
#if CONFIG_ESP_NETIF_BRIDGE_EN
}
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
return ESP_OK;
}
@ -803,9 +887,8 @@ static esp_err_t esp_netif_config_sanity_check(const esp_netif_t * esp_netif)
return ESP_ERR_INVALID_STATE;
}
if (esp_netif->driver_transmit == NULL ||
esp_netif->driver_handle == NULL ||
esp_netif->lwip_input_fn == NULL ||
if ((!(esp_netif->flags & ESP_NETIF_FLAG_IS_BRIDGE) && (esp_netif->driver_transmit == NULL ||
esp_netif->driver_handle == NULL || esp_netif->lwip_input_fn == NULL)) ||
esp_netif->lwip_init_fn == NULL) {
ESP_LOGE(TAG, "Cannot start esp_netif: Missing mandatory configuration:\n"
"esp_netif->driver_transmit: %p, esp_netif->driver_handle:%p, "
@ -847,6 +930,7 @@ static esp_err_t esp_netif_start_api(esp_netif_api_msg_t *msg)
if (esp_netif->flags&ESP_NETIF_FLAG_AUTOUP) {
ESP_LOGD(TAG, "%s Setting the lwip netif%p UP", __func__, p_netif);
netif_set_up(p_netif);
netif_set_link_up(p_netif);
}
if (esp_netif->flags & ESP_NETIF_DHCP_SERVER) {
#if ESP_DHCPS
@ -1394,6 +1478,7 @@ static esp_err_t esp_netif_up_api(esp_netif_api_msg_t *msg)
/* use last obtained ip, or static ip */
netif_set_addr(lwip_netif, (ip4_addr_t*)&esp_netif->ip_info->ip, (ip4_addr_t*)&esp_netif->ip_info->netmask, (ip4_addr_t*)&esp_netif->ip_info->gw);
netif_set_up(lwip_netif);
netif_set_link_up(lwip_netif);
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STARTED);
@ -1431,6 +1516,7 @@ static esp_err_t esp_netif_down_api(esp_netif_api_msg_t *msg)
#endif
netif_set_addr(lwip_netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
netif_set_down(lwip_netif);
netif_set_link_down(lwip_netif);
if (esp_netif->flags & ESP_NETIF_DHCP_CLIENT) {
esp_netif_start_ip_lost_timer(esp_netif);

View File

@ -12,6 +12,7 @@
#include "netif/wlanif.h"
#include "netif/ethernetif.h"
#include "netif/bridgeif.h"
#if CONFIG_OPENTHREAD_ENABLED
#include "netif/openthreadif.h"
#endif
@ -27,6 +28,12 @@ static const struct esp_netif_netstack_config s_eth_netif_config = {
.input_fn = ethernetif_input
}
};
static const struct esp_netif_netstack_config s_br_netif_config = {
.lwip = {
.init_fn = bridgeif_init,
.input_fn = NULL
}
};
static const struct esp_netif_netstack_config s_wifi_netif_config_ap = {
.lwip = {
.init_fn = wlanif_init_ap,
@ -52,6 +59,7 @@ static const struct esp_netif_netstack_config s_netif_config_ppp = {
};
const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_eth = &s_eth_netif_config;
const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_br = &s_br_netif_config;
const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_sta = &s_wifi_netif_config_sta;
const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_wifi_ap = &s_wifi_netif_config_ap;
const esp_netif_netstack_config_t *_g_esp_netif_netstack_default_ppp = &s_netif_config_ppp;

View File

@ -126,6 +126,13 @@ struct esp_netif_obj {
char * if_key;
char * if_desc;
int route_prio;
#if CONFIG_ESP_NETIF_BRIDGE_EN
// bridge configuration
uint16_t max_fdb_dyn_entries;
uint16_t max_fdb_sta_entries;
uint8_t max_ports;
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
};
#endif /* CONFIG_ESP_NETIF_TCPIP_LWIP */

View File

@ -56,6 +56,9 @@ set(srcs
"lwip/src/core/ipv6/mld6.c"
"lwip/src/core/ipv6/nd6.c"
"lwip/src/netif/ethernet.c"
"lwip/src/netif/bridgeif.c"
"lwip/src/netif/bridgeif_fdb.c"
"lwip/src/netif/slipif.c"
"lwip/src/netif/slipif.c"
"lwip/src/netif/ppp/auth.c"
"lwip/src/netif/ppp/ccp.c"

View File

@ -250,6 +250,13 @@ menu "LWIP"
options and values. If your code meets LWIP_ASSERT due to option value is too long.
Please increase the LWIP_DHCP_OPTIONS_LEN value.
config LWIP_NUM_NETIF_CLIENT_DATA
int "Number of clients store data in netif"
default 0
range 0 256
help
Number of clients that may store data in client_data member array of struct netif.
menu "DHCP server"
config LWIP_DHCPS
@ -870,6 +877,13 @@ menu "LWIP"
endmenu # SNTP
config LWIP_BRIDGEIF_MAX_PORTS
int "Maximum number of bridge ports"
default 7
range 1 63
help
Set maximum number of ports a bridge can consists of.
config LWIP_ESP_LWIP_ASSERT
bool "Enable LWIP ASSERT checks"
default y
@ -1075,4 +1089,19 @@ menu "LWIP"
depends on LWIP_DEBUG
default n
config LWIP_BRIDGEIF_DEBUG
bool "Enable bridge generic debug messages"
depends on LWIP_DEBUG
default n
config LWIP_BRIDGEIF_FDB_DEBUG
bool "Enable bridge FDB debug messages"
depends on LWIP_DEBUG
default n
config LWIP_BRIDGEIF_FW_DEBUG
bool "Enable bridge forwarding debug messages"
depends on LWIP_DEBUG
default n
endmenu

View File

@ -661,6 +661,22 @@ static inline uint32_t timeout_from_offered(uint32_t lease, uint32_t min)
*/
#define LWIP_NETIF_TX_SINGLE_PBUF 1
/**
* LWIP_NUM_NETIF_CLIENT_DATA: Number of clients that may store
* data in client_data member array of struct netif (max. 256).
*/
#ifdef CONFIG_LWIP_NUM_NETIF_CLIENT_DATA
#define LWIP_NUM_NETIF_CLIENT_DATA CONFIG_LWIP_NUM_NETIF_CLIENT_DATA
#endif
/**
* BRIDGEIF_MAX_PORTS: this is used to create a typedef used for forwarding
* bit-fields: the number of bits required is this + 1 (for the internal/cpu port)
*/
#ifdef CONFIG_LWIP_BRIDGEIF_MAX_PORTS
#define BRIDGEIF_MAX_PORTS CONFIG_LWIP_BRIDGEIF_MAX_PORTS
#endif
/*
------------------------------------
---------- LOOPIF options ----------
@ -1328,6 +1344,27 @@ static inline uint32_t timeout_from_offered(uint32_t lease, uint32_t min)
*/
#define TCP_OOSEQ_DEBUG LWIP_DBG_OFF
/**
* BRIDGEIF_DEBUG: Enable generic debugging for bridge.
*/
#ifdef CONFIG_LWIP_BRIDGEIF_DEBUG
#define BRIDGEIF_DEBUG LWIP_DBG_ON
#endif
/**
* BRIDGEIF_FDB_DEBUG: Enable debugging for bridge FDB.
*/
#ifdef CONFIG_LWIP_BRIDGEIF_FDB_DEBUG
#define BRIDGEIF_FDB_DEBUG LWIP_DBG_ON
#endif
/**
* BRIDGEIF_FW_DEBUG: Enable debugging for bridge forwarding.
*/
#ifdef CONFIG_LWIP_BRIDGEIF_FW_DEBUG
#define BRIDGEIF_FW_DEBUG LWIP_DBG_ON
#endif
/*
--------------------------------------
------------ SNTP options ------------

View File

@ -74,7 +74,7 @@ static void ethernet_low_level_init(struct netif *netif)
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
#if ESP_LWIP
#if LWIP_IGMP

View File

@ -61,7 +61,7 @@ low_level_init(struct netif *netif)
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#if ESP_LWIP
#if LWIP_IGMP

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(bridge)

View File

@ -0,0 +1,145 @@
# Bridge Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates basic usage of `LwIP IEEE 802.1D bridge`. Bridge connects two separate networks to appear as if they were a single network at layer 2.
Specifically, from IOT use cases point of view, one of the advantages of bridge usage is to enable ring topology of Ethernet connected devices which would otherwise needed to be connected in tree topology. Tree topology usually requires extensive wiring since each device is connected to the central point (switch/router) by separate cable. In opposite, ring topology can save wiring since the devices can be "daisy-chained" (each device in path can pass the traffic further to the final destination). Note that the ring may not be "closed" and so simplifies network topology even further.
Performance of this type of "software" bridge is limited by the performance of ESP32 and data bus used to interconnect ESP32 with physical network interfaces (e.g. SPI shared among multiple Ethernet modules). If your application requires higher performance network, please consider using switch ICs which are specifically designed for such applications like [KSZ8863](https://github.com/espressif/esp-eth-drivers/blob/master/ksz8863/).
## How to use example
You need one ESP32 with at least two Ethernet ports and two PCs (or other Ethernet capable devices). Connect the network as shown in figure below, configure PC#1 as DHCP server and PC#2 as DHCP client.
![example network](docs/network.png)
The work flow of the example is then as follows:
1. Install the Ethernet ports drivers in ESP32.
2. Configure bridge.
3. Wait for a DHCP leases in ESP32 and PC#2.
4. If get IP addresses successfully, then you will be able to ping the ESP32 device and PC#2 from PC#1 (and vice versa).
## Hardware Required
To run this example, it's recommended that you have either an official ESP32 Ethernet development board - [ESP32-Ethernet-Kit](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/get-started-ethernet-kit.html), or 3rd party ESP32 board as long as it's integrated with a supported Ethernet PHY chips, connected with supported SPI Ethernet modules (for example `DM9051`, `W5500` or `KSZ8851SNL`). Or ESP32(S/C series) board without internal Ethernet interface but connected to multiple SPI Ethernet modules.
### Pin Assignment
See common pin assignments for [Ethernet examples](../../ethernet/README.md#common-pin-assignments).
When using two Ethernet SPI modules at a time, they are to be connected to single SPI interface. Both modules then share data (MOSI/MISO) and CLK signals. However, the CS, interrupt and reset pins need to be connected to separate GPIO for each Ethernet SPI module.
## Configure the project
```
idf.py menuconfig
```
To be the bridge feature available, enable `LwIP IEEE 802.1D bridge` option in ESP-NETIF Adapter component config menu and properly configure `Number of clients store data in netif` in LWIP component config menu.
For Ethernet configuration, see common configurations in [Ethernet examples](../../ethernet/README.md#common-configurations).
## Build, Flash, and Run
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
**ESP32 output:**
```bash
I (436) esp_eth.netif.netif_glue: 08:3a:f2:31:20:f7
I (436) esp_eth.netif.netif_glue: ethernet attached to netif
I (436) w5500.mac: version=4
I (446) esp_eth.netif.netif_glue: 08:3a:f2:31:20:f7
I (446) esp_eth.netif.netif_glue: ethernet attached to netif
I (446) esp_netif_br_glue: 08:3a:f2:31:20:f7
I (456) esp_netif_br_glue: bridge netif glue attached
I (3456) eth_example: Ethernet Started
I (3456) eth_example: Ethernet Link Up
I (3456) eth_example: Ethernet HW Addr 08:3a:f2:31:20:f7
I (3466) eth_example: Ethernet Started
I (5466) eth_example: Ethernet Link Up
I (5466) eth_example: Ethernet HW Addr 08:3a:f2:31:20:f7
I (8896) esp_netif_handlers: br0 ip: 192.168.20.105, mask: 255.255.255.0, gw: 192.168.20.1
I (8896) eth_example: Ethernet Got IP Address
I (8896) eth_example: ~~~~~~~~~~~
I (8896) eth_example: ETHIP:192.168.20.105
I (8906) eth_example: ETHMASK:255.255.255.0
I (8906) eth_example: ETHGW:192.168.20.1
I (8916) eth_example: ~~~~~~~~~~~
```
**PC output (on Linux OS):**
```bash
$ ip a
...
2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 70:85:c2:d3:ea:18 brd ff:ff:ff:ff:ff:ff
inet 192.168.20.116/24 brd 192.168.20.255 scope global dynamic noprefixroute enp4s0
valid_lft 346sec preferred_lft 346sec
inet6 fe80::4efa:2bae:e58c:231e/64 scope link noprefixroute
valid_lft forever preferred_lft forever
...
```
**PC output (on Windows OS):**
```bash
ipconfig -all
...
Ethernet adapter Ethernet:
Connection-specific DNS Suffix . : example.org
Description . . . . . . . . . . . : Realtek PCIe GbE Family Controller
Physical Address. . . . . . . . . : 70-85-C2-D3-EA-18
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : fe80::21e3:aa78:f165:bbc8%15(Preferred)
IPv4 Address. . . . . . . . . . . : 192.168.20.116(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Lease Obtained. . . . . . . . . . : Tuesday, May 10, 2022 10:36:52 AM
Lease Expires . . . . . . . . . . : Tuesday, May 10, 2022 10:46:51 AM
Default Gateway . . . . . . . . . :
DHCP Server . . . . . . . . . . . : 192.168.20.1
DHCPv6 IAID . . . . . . . . . . . : 259032514
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-25-3F-B4-00-70-85-C2-D3-EA-18
DNS Servers . . . . . . . . . . . : fec0:0:0:ffff::1%1
fec0:0:0:ffff::2%1
fec0:0:0:ffff::3%1
NetBIOS over Tcpip. . . . . . . . : Enabled
...
```
Now you can ping your ESP32 in PC#1 terminal by entering `ping 192.168.20.105` and you can ping your PC#2 in PC#1 terminal by entering `ping 192.168.20.116` (note that actual IP addresses depend on what you get by DHCP server).
## Known Limitations
* Currently only Ethernet interfaces can be bridged using LwIP bridge.
* If you need to stop just one Ethernet interface which is bridged to perform some action like speed/duplex setting, **all remaining interfaces** associated with the bridge need to be stopped as well to the bridge work properly after the interfaces are started again.
## Troubleshooting
See common troubleshooting for [Ethernet examples](../../ethernet/README.md#common-troubleshooting).
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)

View File

@ -0,0 +1,61 @@
<mxfile host="65bd71144e">
<diagram id="qvqIMWZhLYQI7_66ceEl" name="Page-1">
<mxGraphModel dx="1074" dy="527" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="#B8B8B8" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="ESP32 w/ 2 bridged Eth ports&lt;br&gt;(DHCP Client)" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="280" y="160" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="5" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="3" target="2" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="PC#1&lt;br&gt;(DHCP Server)" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="160" y="280" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="4" target="2" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="4" value="PC#2&lt;br&gt;(DHCP Client)" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="400" y="280" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="7" value="" style="endArrow=open;startArrow=open;html=1;startFill=0;endFill=0;dashed=1;dashPattern=1 1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="290" y="309.71" as="sourcePoint"/>
<mxPoint x="390" y="309.71" as="targetPoint"/>
<Array as="points">
<mxPoint x="340" y="309.71"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="8" value="" style="endArrow=open;startArrow=open;html=1;startFill=0;endFill=0;dashed=1;dashPattern=1 1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="180" y="270" as="sourcePoint"/>
<mxPoint x="270" y="190" as="targetPoint"/>
<Array as="points">
<mxPoint x="210" y="210"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="9" value="" style="endArrow=open;startArrow=open;html=1;startFill=0;endFill=0;dashed=1;dashPattern=1 1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="410" y="190" as="sourcePoint"/>
<mxPoint x="500" y="270" as="targetPoint"/>
<Array as="points">
<mxPoint x="470" y="210"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="11" value="ping" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="160" y="190" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="12" value="ping" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="460" y="190" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="13" value="ping" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="310" y="280" width="60" height="30" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "bridge_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,241 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config EXAMPLE_USE_INTERNAL_ETHERNET
depends on IDF_TARGET_ESP32
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Use internal Ethernet MAC controller.
if EXAMPLE_USE_INTERNAL_ETHERNET
choice EXAMPLE_ETH_PHY_MODEL
prompt "Ethernet PHY Device"
default EXAMPLE_ETH_PHY_IP101
help
Select the Ethernet PHY device to use in the example.
config EXAMPLE_ETH_PHY_IP101
bool "IP101"
help
IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
config EXAMPLE_ETH_PHY_RTL8201
bool "RTL8201/SR8201"
help
RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
config EXAMPLE_ETH_PHY_LAN87XX
bool "LAN87xx"
help
Below chips are supported:
LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
flexPWR® Technology.
LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver
with HP Auto-MDIX and flexPWR® Technology.
LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
flexPWR® Technology.
Goto https://www.microchip.com for more information about them.
config EXAMPLE_ETH_PHY_DP83848
bool "DP83848"
help
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
Goto http://www.ti.com/product/DP83848J for more information about it.
config EXAMPLE_ETH_PHY_KSZ80XX
bool "KSZ80xx"
help
With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX
Ethernet Physical Layer Tranceivers (PHY).
The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041,
KSZ8051, KSZ8061, KSZ8081, KSZ8091
Goto https://www.microchip.com for more information about them.
endchoice # EXAMPLE_ETH_PHY_MODEL
config EXAMPLE_ETH_MDC_GPIO
int "SMI MDC GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 23
help
Set the GPIO number used by SMI MDC.
config EXAMPLE_ETH_MDIO_GPIO
int "SMI MDIO GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 18
help
Set the GPIO number used by SMI MDIO.
config EXAMPLE_ETH_PHY_RST_GPIO
int "PHY Reset GPIO number"
range -1 ENV_GPIO_OUT_RANGE_MAX
default 5
help
Set the GPIO number used to reset PHY chip.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_PHY_ADDR
int "PHY Address"
range 0 31
default 1
help
Set PHY address according your board schematic.
endif # EXAMPLE_USE_INTERNAL_ETHERNET
config EXAMPLE_USE_SPI_ETHERNET
bool "SPI Ethernet"
default y
select ETH_USE_SPI_ETHERNET
help
Use external SPI-Ethernet module(s).
if EXAMPLE_USE_SPI_ETHERNET
config EXAMPLE_SPI_ETHERNETS_NUM
int "Number of SPI Ethernet modules to use at a time"
range 1 2
default 1
help
Set the number of SPI Ethernet modules you want to use at a time. Multiple SPI modules can be connected
to one SPI interface and can be separately accessed based on state of associated Chip Select (CS).
choice EXAMPLE_ETHERNET_TYPE_SPI
prompt "Ethernet SPI"
default EXAMPLE_USE_W5500
help
Select which kind of Ethernet will be used in the example.
config EXAMPLE_USE_DM9051
bool "DM9051 Module"
select ETH_SPI_ETHERNET_DM9051
help
Select external SPI-Ethernet module (DM9051).
config EXAMPLE_USE_KSZ8851SNL
bool "KSZ8851SNL Module"
select ETH_SPI_ETHERNET_KSZ8851SNL
help
Select external SPI-Ethernet module (KSZ8851SNL).
config EXAMPLE_USE_W5500
bool "W5500 Module"
select ETH_SPI_ETHERNET_W5500
help
Select external SPI-Ethernet module (W5500).
endchoice
config EXAMPLE_ETH_SPI_HOST
int "SPI Host Number"
range 0 2
default 1
help
Set the SPI host used to communicate with the SPI Ethernet Controller.
config EXAMPLE_ETH_SPI_SCLK_GPIO
int "SPI SCLK GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 14 if IDF_TARGET_ESP32
default 12 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 6 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2
help
Set the GPIO number used by SPI SCLK.
config EXAMPLE_ETH_SPI_MOSI_GPIO
int "SPI MOSI GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 13 if IDF_TARGET_ESP32
default 11 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 7 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2
help
Set the GPIO number used by SPI MOSI.
config EXAMPLE_ETH_SPI_MISO_GPIO
int "SPI MISO GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 12 if IDF_TARGET_ESP32
default 13 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 2 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2
help
Set the GPIO number used by SPI MISO.
config EXAMPLE_ETH_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 80
default 12 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C2
default 36 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
help
Set the clock speed (MHz) of SPI interface.
config EXAMPLE_ETH_SPI_CS0_GPIO
int "SPI CS0 GPIO number for SPI Ethernet module #1"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 15 if IDF_TARGET_ESP32
default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2
help
Set the GPIO number used by SPI CS0, i.e. Chip Select associated with the first SPI Eth module).
config EXAMPLE_ETH_SPI_CS1_GPIO
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
int "SPI CS1 GPIO number for SPI Ethernet module #2"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 32 if IDF_TARGET_ESP32
default 7 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 8 if IDF_TARGET_ESP32C3
default 3 if IDF_TARGET_ESP32C2
help
Set the GPIO number used by SPI CS1, i.e. Chip Select associated with the second SPI Eth module.
config EXAMPLE_ETH_SPI_INT0_GPIO
int "Interrupt GPIO number SPI Ethernet module #1"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 4 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
default 4 if IDF_TARGET_ESP32C2
help
Set the GPIO number used by the first SPI Ethernet module interrupt line.
config EXAMPLE_ETH_SPI_INT1_GPIO
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
int "Interrupt GPIO number SPI Ethernet module #2"
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
default 33 if IDF_TARGET_ESP32
default 5 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2
help
Set the GPIO number used by the second SPI Ethernet module interrupt line.
config EXAMPLE_ETH_SPI_PHY_RST0_GPIO
int "PHY Reset GPIO number of SPI Ethernet Module #1"
range -1 ENV_GPIO_OUT_RANGE_MAX
default -1
help
Set the GPIO number used to reset PHY chip on the first SPI Ethernet module.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_SPI_PHY_RST1_GPIO
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
int "PHY Reset GPIO number of SPI Ethernet Module #2"
range -1 ENV_GPIO_OUT_RANGE_MAX
default -1
help
Set the GPIO number used to reset PHY chip on the second SPI Ethernet module.
Set to -1 to disable PHY chip hardware reset.
config EXAMPLE_ETH_SPI_PHY_ADDR0
int "PHY Address of SPI Ethernet Module #1"
range 0 31
default 1
help
Set the first SPI Ethernet module PHY address according your board schematic.
config EXAMPLE_ETH_SPI_PHY_ADDR1
depends on EXAMPLE_SPI_ETHERNETS_NUM > 1
int "PHY Address of SPI Ethernet Module #2"
range 0 31
default 1
help
Set the second SPI Ethernet module PHY address according your board schematic.
endif # EXAMPLE_USE_SPI_ETHERNET
endmenu

View File

@ -0,0 +1,334 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_netif_br_glue.h"
#include "esp_eth.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#if CONFIG_ETH_USE_SPI_ETHERNET
#include "driver/spi_master.h"
#endif // CONFIG_ETH_USE_SPI_ETHERNET
static const char *TAG = "eth_bridge_example";
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM
#define SPI_ETHERNETS_NUM CONFIG_EXAMPLE_SPI_ETHERNETS_NUM
#else
#define SPI_ETHERNETS_NUM 0
#endif
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
#define INTERNAL_ETHERNETS_NUM 1
#else
#define INTERNAL_ETHERNETS_NUM 0
#endif
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
#define INIT_SPI_ETH_MODULE_CONFIG(eth_module_config, num) \
do { \
eth_module_config[num].spi_cs_gpio = CONFIG_EXAMPLE_ETH_SPI_CS ##num## _GPIO; \
eth_module_config[num].int_gpio = CONFIG_EXAMPLE_ETH_SPI_INT ##num## _GPIO; \
eth_module_config[num].phy_reset_gpio = CONFIG_EXAMPLE_ETH_SPI_PHY_RST ##num## _GPIO; \
eth_module_config[num].phy_addr = CONFIG_EXAMPLE_ETH_SPI_PHY_ADDR ##num; \
} while(0)
typedef struct {
uint8_t spi_cs_gpio;
uint8_t int_gpio;
int8_t phy_reset_gpio;
uint8_t phy_addr;
}spi_eth_module_config_t;
#endif
/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
uint8_t mac_addr[6] = {0};
/* we can get the ethernet driver handle from event data */
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet (%p) Link Up", eth_handle);
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet (%p) Link Down", eth_handle);
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet (%p) Started", eth_handle);
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet (%p) Stopped", eth_handle);
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
}
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
/** Internal EMAC initialization */
esp_eth_handle_t eth_init_internal(void)
{
esp_eth_handle_t eth_handle;
// Init MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
#if CONFIG_EXAMPLE_ETH_PHY_IP101
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX
esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
esp_eth_phy_t *phy = esp_eth_phy_new_dp83848(&phy_config);
#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX
esp_eth_phy_t *phy = esp_eth_phy_new_ksz80xx(&phy_config);
#endif
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
return eth_handle;
}
#endif // CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
/** Ethernet SPI modules initialization */
esp_eth_handle_t eth_init_spi(spi_eth_module_config_t *spi_eth_module_config, uint8_t *mac_addr)
{
esp_eth_handle_t eth_handle;
// Init MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
// Configure SPI interface and Ethernet driver for specific SPI module
esp_eth_mac_t *mac;
esp_eth_phy_t *phy;
spi_device_handle_t spi_handle;
#if CONFIG_EXAMPLE_USE_KSZ8851SNL
spi_device_interface_config_t devcfg = {
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.queue_size = 20
};
// Set SPI module Chip Select GPIO
devcfg.spics_io_num = spi_eth_module_config->spi_cs_gpio;
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
// KSZ8851SNL ethernet driver is based on spi driver
eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(spi_handle);
// Set remaining GPIO numbers and configuration used by the SPI module
ksz8851snl_config.int_gpio_num = spi_eth_module_config->int_gpio;
phy_config.phy_addr = spi_eth_module_config->phy_addr;
phy_config.reset_gpio_num = spi_eth_module_config->phy_reset_gpio;
mac = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config);
phy = esp_eth_phy_new_ksz8851snl(&phy_config);
#elif CONFIG_EXAMPLE_USE_DM9051
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.queue_size = 20
};
// Set SPI module Chip Select GPIO
devcfg.spics_io_num = spi_eth_module_config->spi_cs_gpio;
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
// dm9051 ethernet driver is based on spi driver
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
// Set remaining GPIO numbers and configuration used by the SPI module
dm9051_config.int_gpio_num = spi_eth_module_config->int_gpio;
phy_config.phy_addr = spi_eth_module_config->phy_addr;
phy_config.reset_gpio_num = spi_eth_module_config->phy_reset_gpio;
mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
phy = esp_eth_phy_new_dm9051(&phy_config);
#elif CONFIG_EXAMPLE_USE_W5500
spi_device_interface_config_t devcfg = {
.command_bits = 16, // Actually it's the address phase in W5500 SPI frame
.address_bits = 8, // Actually it's the control phase in W5500 SPI frame
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.queue_size = 20
};
// Set SPI module Chip Select GPIO
devcfg.spics_io_num = spi_eth_module_config->spi_cs_gpio;
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
// w5500 ethernet driver is based on spi driver
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
// Set remaining GPIO numbers and configuration used by the SPI module
w5500_config.int_gpio_num = spi_eth_module_config->int_gpio;
phy_config.phy_addr = spi_eth_module_config->phy_addr;
phy_config.reset_gpio_num = spi_eth_module_config->phy_reset_gpio;
mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
phy = esp_eth_phy_new_w5500(&phy_config);
#endif //CONFIG_EXAMPLE_USE_W5500
esp_eth_config_t eth_config_spi = ETH_DEFAULT_CONFIG(mac, phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config_spi, &eth_handle));
// The SPI Ethernet module might not have a burned factory MAC address, we cat to set it manually.
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr));
return eth_handle;
}
#endif // CONFIG_EXAMPLE_USE_SPI_ETHERNET
void app_main(void)
{
// number of Ethernet ports to be used in the bridge
uint8_t port_cnt = 0;
// the same MAC address will be used for all Ethernet ports since the bridge acts as one device
uint8_t common_mac_addr[ETH_ADDR_LEN];
esp_eth_handle_t eth_handles[SPI_ETHERNETS_NUM + INTERNAL_ETHERNETS_NUM] = { NULL };
esp_netif_t *eth_netifs[SPI_ETHERNETS_NUM + INTERNAL_ETHERNETS_NUM] = { NULL };
// Initialize TCP/IP network interface (should be called only once in application)
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop that running in background
ESP_ERROR_CHECK(esp_event_loop_create_default());
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
eth_handles[port_cnt++] = eth_init_internal();
// use burned ESP32 MAC address as commom address for all Ethernet interfaces
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handles[0], ETH_CMD_G_MAC_ADDR, common_mac_addr));
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
// if ESP32 internal Ethernet is not used, use manually configured MAC address
// 02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control.
memcpy(common_mac_addr, (uint8_t[]) {0x02, 0x00, 0x00, 0x12, 0x34, 0x56}, ETH_ADDR_LEN);
#endif //CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
#if CONFIG_EXAMPLE_USE_SPI_ETHERNET
// Install GPIO ISR handler to be able to service SPI Eth modlues interrupts
gpio_install_isr_service(0);
// Init SPI bus
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
// Init specific SPI Ethernet module configuration from Kconfig (CS GPIO, Interrupt GPIO, etc.)
spi_eth_module_config_t spi_eth_module_config[CONFIG_EXAMPLE_SPI_ETHERNETS_NUM];
INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 0);
#if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 1
INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 1);
#endif
for (int i = 0; i < CONFIG_EXAMPLE_SPI_ETHERNETS_NUM; i++) {
eth_handles[port_cnt++] = eth_init_spi(&spi_eth_module_config[i], common_mac_addr);
}
#endif // CONFIG_ETH_USE_SPI_ETHERNET
// Create instances of esp-netif for Ethernet ports
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
esp_netif_config_t netif_cfg = {
.base = &esp_netif_config,
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
};
char if_key_str[10];
char if_desc_str[10];
char num_str[3];
for (int i = 0; i < port_cnt; i++) {
itoa(i, num_str, 10);
strcat(strcpy(if_key_str, "ETH_"), num_str);
strcat(strcpy(if_desc_str, "eth"), num_str);
esp_netif_config.if_key = if_key_str;
esp_netif_config.if_desc = if_desc_str;
esp_netif_config.route_prio = 50 - i;
esp_netif_config.flags = 0; // ESP-NETIF flags need to be zero when port's to be bridged
eth_netifs[i] = esp_netif_new(&netif_cfg);
// attach Ethernet driver to TCP/IP stack
ESP_ERROR_CHECK(esp_netif_attach(eth_netifs[i], esp_eth_new_netif_glue(eth_handles[i])));
}
// Create instance of esp-netif for bridge interface
esp_netif_inherent_config_t esp_netif_br_config = ESP_NETIF_INHERENT_DEFAULT_BR();
esp_netif_config_t netif_br_cfg = {
.base = &esp_netif_br_config,
.stack = ESP_NETIF_NETSTACK_DEFAULT_BR,
};
bridgeif_config_t bridgeif_config = {
.max_fdb_dyn_entries = 10,
.max_fdb_sta_entries = 2,
.max_ports = port_cnt
};
esp_netif_br_config.bridge_info = &bridgeif_config;
// Set MAC address of bridge interface the same as the Ethernet interface
memcpy(esp_netif_br_config.mac, common_mac_addr, ETH_ADDR_LEN);
esp_netif_t *br_netif = esp_netif_new(&netif_br_cfg);
// Create new esp netif bridge glue instance
esp_netif_br_glue_handle_t netif_br_glue = esp_netif_br_glue_new();
// Add Ethernet port interfaces to that esp netif bridge glue instance
for (int i = 0; i < port_cnt; i++) {
ESP_ERROR_CHECK(esp_netif_br_glue_add_port(netif_br_glue, eth_netifs[i]));
}
// Attach esp netif bridge glue instance with added ports to bridge netif
ESP_ERROR_CHECK(esp_netif_attach(br_netif, netif_br_glue));
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
for (int i = 0; i < port_cnt; i++) {
// Since the MAC forwarding is performed in lwIP bridge, we need to pass all addresses through the Ethernet MACs
bool promiscuous = true;
esp_eth_ioctl(eth_handles[i], ETH_CMD_S_PROMISCUOUS, &promiscuous);
// Start Ethernet driver state machine
ESP_ERROR_CHECK(esp_eth_start(eth_handles[i]));
}
}

View File

@ -0,0 +1,4 @@
CONFIG_ESP_NETIF_TCPIP_LWIP=y
CONFIG_ESP_NETIF_BRIDGE_EN=y
CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=1