forked from espressif/esp-idf
Merge branch 'feat/eth_mac_filter' into 'master'
feat(esp_eth): added option to set MAC filter for mcast Closes IDF-12503 and IDFGH-14897 See merge request espressif/esp-idf!36944
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -175,6 +175,9 @@ typedef enum {
|
||||
ETH_CMD_S_PHY_LOOPBACK, /*!< Set PHY loopback */
|
||||
ETH_CMD_READ_PHY_REG, /*!< Read PHY register */
|
||||
ETH_CMD_WRITE_PHY_REG, /*!< Write PHY register */
|
||||
ETH_CMD_S_ALL_MULTICAST, /*!< Set receive all multicast */
|
||||
ETH_CMD_ADD_MAC_FILTER, /*!< Add MAC filter */
|
||||
ETH_CMD_DEL_MAC_FILTER, /*!< Delete MAC filter */
|
||||
|
||||
ETH_CMD_CUSTOM_MAC_CMDS = ETH_CMD_CUSTOM_MAC_CMDS_OFFSET, // Offset for start of MAC custom commands
|
||||
ETH_CMD_CUSTOM_PHY_CMDS = ETH_CMD_CUSTOM_PHY_CMDS_OFFSET, // Offset for start of PHY custom commands
|
||||
@ -380,6 +383,9 @@ esp_err_t esp_eth_transmit_ctrl_vargs(esp_eth_handle_t hdl, void *ctrl, uint32_t
|
||||
* Preconditions: Ethernet driver needs to be stopped and auto-negotiation disabled.
|
||||
* @li @c ETH_CMD_G_DUPLEX_MODE gets current Ethernet link duplex mode. @c data argument is pointer to memory of eth_duplex_t datatype to which the duplex mode is to be stored.
|
||||
* @li @c ETH_CMD_S_PHY_LOOPBACK sets/resets PHY to/from loopback mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
* @li @c ETH_CMD_S_ALL_MULTICAST sets/resets Ethernet interface to/from receive all multicast mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
* @li @c ETH_CMD_ADD_MAC_FILTER adds a MAC address to the MAC filter. @c data argument is pointer to MAC address buffer with expected size of 6 bytes.
|
||||
* @li @c ETH_CMD_DEL_MAC_FILTER deletes a MAC address from the MAC filter. @c data argument is pointer to MAC address buffer with expected size of 6 bytes.
|
||||
*
|
||||
* @li Note that additional control commands may be available for specific MAC or PHY chips. Please consult specific MAC or PHY documentation or driver code.
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -231,6 +231,30 @@ struct esp_eth_mac_s {
|
||||
*/
|
||||
esp_err_t (*get_addr)(esp_eth_mac_t *mac, uint8_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Add Destination address MAC filter
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] addr: MAC address
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: add MAC filter successfully
|
||||
* - ESP_FAIL: add MAC filter failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*add_mac_filter)(esp_eth_mac_t *mac, uint8_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Remove Destination address MAC filter
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] addr: MAC address
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: remove MAC filter successfully
|
||||
* - ESP_FAIL: remove MAC filter failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*rm_mac_filter)(esp_eth_mac_t *mac, uint8_t *addr);
|
||||
|
||||
/**
|
||||
* @brief Set speed of MAC
|
||||
*
|
||||
@ -286,6 +310,18 @@ struct esp_eth_mac_s {
|
||||
*/
|
||||
esp_err_t (*set_promiscuous)(esp_eth_mac_t *mac, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set receive all multicast
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] enable: set true to enable receive all multicast; set false to disable receive all multicast
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: set receive all multicast successfully
|
||||
* - ESP_FAIL: set receive all multicast failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*set_all_multicast)(esp_eth_mac_t *mac, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable flow control on MAC layer or not
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -483,6 +483,11 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set promiscuous to null");
|
||||
ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
|
||||
break;
|
||||
case ETH_CMD_S_ALL_MULTICAST:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set all multicast to null");
|
||||
ESP_GOTO_ON_FALSE(mac->set_all_multicast != NULL, ESP_ERR_NOT_SUPPORTED, err, TAG, "set receive all multicast not supported");
|
||||
ESP_GOTO_ON_ERROR(mac->set_all_multicast(mac, *(bool *)data), err, TAG, "set all multicast mode failed");
|
||||
break;
|
||||
case ETH_CMD_S_FLOW_CTRL:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set flow ctrl to null");
|
||||
ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
|
||||
@ -523,6 +528,18 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
||||
phy_addr, phy_w_data->reg_addr, *(phy_w_data->reg_value_p)), err, TAG, "failed to write PHY register");
|
||||
}
|
||||
break;
|
||||
case ETH_CMD_ADD_MAC_FILTER: {
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
|
||||
ESP_GOTO_ON_FALSE(mac->add_mac_filter != NULL, ESP_ERR_NOT_SUPPORTED, err, TAG, "add mac address to filter not supported");
|
||||
ESP_GOTO_ON_ERROR(mac->add_mac_filter(mac, (uint8_t *)data), err, TAG, "add mac address to filter failed");
|
||||
}
|
||||
break;
|
||||
case ETH_CMD_DEL_MAC_FILTER: {
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set mac addr to null");
|
||||
ESP_GOTO_ON_FALSE(mac->rm_mac_filter != NULL, ESP_ERR_NOT_SUPPORTED, err, TAG, "remove mac address from filter not supported");
|
||||
ESP_GOTO_ON_ERROR(mac->rm_mac_filter(mac, (uint8_t *)data), err, TAG, "remove mac address from filter failed");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (phy->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_PHY_CMDS) {
|
||||
ret = phy->custom_ioctl(phy, cmd, data);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -46,9 +46,23 @@ static void eth_l2_free(void *h, void* buffer)
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t eth_set_mac_filter(void *h, const uint8_t *eth_mac, size_t mac_len, bool add)
|
||||
{
|
||||
esp_eth_handle_t *eth_handle = (esp_eth_handle_t *)h;
|
||||
ESP_RETURN_ON_FALSE(mac_len == ETH_ADDR_LEN, ESP_ERR_INVALID_ARG, TAG, "invalid MAC length");
|
||||
ESP_LOGD(TAG, "%s filter MAC: %02x:%02x:%02x:%02x:%02x:%02x", add ? "Add" : "Del", eth_mac[0], eth_mac[1],
|
||||
eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
|
||||
if (add) {
|
||||
ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, (void *)eth_mac), TAG, "failed to add mac filter");
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, (void *)eth_mac), TAG, "failed to delete mac filter");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_eth_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
uint8_t eth_mac[6];
|
||||
uint8_t eth_mac[ETH_ADDR_LEN];
|
||||
esp_eth_netif_glue_t *netif_glue = (esp_eth_netif_glue_t *)args;
|
||||
netif_glue->base.netif = esp_netif;
|
||||
|
||||
@ -58,7 +72,8 @@ static esp_err_t esp_eth_post_attach(esp_netif_t *esp_netif, void *args)
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = netif_glue->eth_driver,
|
||||
.transmit = esp_eth_transmit,
|
||||
.driver_free_rx_buffer = eth_l2_free
|
||||
.driver_free_rx_buffer = eth_l2_free,
|
||||
.driver_set_mac_filter = eth_set_mac_filter
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
|
||||
|
@ -47,6 +47,8 @@ static const char *TAG = "esp.emac";
|
||||
#define RMII_10M_SPEED_RX_TX_CLK_DIV (19)
|
||||
#define RMII_100M_SPEED_RX_TX_CLK_DIV (1)
|
||||
|
||||
#define EMAC_MULTI_REG_MUTEX_TIMEOUT_MS (100)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
// ESP32P4 EMAC interface clock configuration is shared among other modules in registers
|
||||
#define EMAC_IF_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
@ -70,6 +72,7 @@ typedef struct {
|
||||
bool flow_ctrl_enabled; // indicates whether the user want to do flow control
|
||||
bool do_flow_ctrl; // indicates whether we need to do software flow control
|
||||
bool use_pll; // Only use (A/M)PLL in EMAC_DATA_INTERFACE_RMII && EMAC_CLK_OUT
|
||||
SemaphoreHandle_t multi_reg_mutex; // lock for multiple register access
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
#endif
|
||||
@ -89,6 +92,16 @@ static void emac_esp_free_driver_obj(emac_esp32_t *emac);
|
||||
static esp_err_t emac_esp32_start(esp_eth_mac_t *mac);
|
||||
static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac);
|
||||
|
||||
static esp_err_t emac_esp32_lock_multi_reg(emac_esp32_t *emac)
|
||||
{
|
||||
return xSemaphoreTake(emac->multi_reg_mutex, pdMS_TO_TICKS(EMAC_MULTI_REG_MUTEX_TIMEOUT_MS)) == pdTRUE ? ESP_OK : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_unlock_multi_reg(emac_esp32_t *emac)
|
||||
{
|
||||
return xSemaphoreGive(emac->multi_reg_mutex) == pdTRUE ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -167,6 +180,28 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_add_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
|
||||
ESP_RETURN_ON_ERROR(emac_esp32_lock_multi_reg(emac), TAG, "failed to lock multiple register access");
|
||||
ESP_GOTO_ON_ERROR(emac_hal_add_addr_da_filter_auto(&emac->hal, addr), err, TAG, "failed to add MAC filter");
|
||||
err:
|
||||
emac_esp32_unlock_multi_reg(emac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_rm_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
|
||||
ESP_RETURN_ON_ERROR(emac_esp32_lock_multi_reg(emac), TAG, "failed to lock multiple register access");
|
||||
ESP_GOTO_ON_ERROR(emac_hal_rm_addr_da_filter_auto(&emac->hal, addr), err, TAG, "failed to remove MAC filter");
|
||||
err:
|
||||
emac_esp32_unlock_multi_reg(emac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_set_link(esp_eth_mac_t *mac, eth_link_t link)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -233,6 +268,13 @@ static esp_err_t emac_esp32_set_promiscuous(esp_eth_mac_t *mac, bool enable)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_set_all_multicast(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
|
||||
emac_hal_pass_all_multicast_enable(&emac->hal, enable);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
|
||||
@ -631,6 +673,10 @@ static void emac_esp_free_driver_obj(emac_esp32_t *emac)
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
if (emac->multi_reg_mutex) {
|
||||
vSemaphoreDelete(emac->multi_reg_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
if (emac->pm_lock) {
|
||||
esp_pm_lock_delete(emac->pm_lock);
|
||||
@ -660,6 +706,10 @@ static esp_err_t emac_esp_alloc_driver_obj(const eth_mac_config_t *config, emac_
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "emac_esp32", &emac->pm_lock), err, TAG, "create pm lock failed");
|
||||
#endif
|
||||
|
||||
emac->multi_reg_mutex = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(emac->multi_reg_mutex, ESP_ERR_NO_MEM, err, TAG, "failed to create multiple register access mutex");
|
||||
|
||||
/* create rx task */
|
||||
BaseType_t core_num = tskNO_AFFINITY;
|
||||
if (config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
|
||||
@ -805,10 +855,13 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
|
||||
emac->parent.read_phy_reg = emac_esp32_read_phy_reg;
|
||||
emac->parent.set_addr = emac_esp32_set_addr;
|
||||
emac->parent.get_addr = emac_esp32_get_addr;
|
||||
emac->parent.add_mac_filter = emac_esp32_add_mac_filter;
|
||||
emac->parent.rm_mac_filter = emac_esp32_rm_mac_filter;
|
||||
emac->parent.set_speed = emac_esp32_set_speed;
|
||||
emac->parent.set_duplex = emac_esp32_set_duplex;
|
||||
emac->parent.set_link = emac_esp32_set_link;
|
||||
emac->parent.set_promiscuous = emac_esp32_set_promiscuous;
|
||||
emac->parent.set_all_multicast = emac_esp32_set_all_multicast;
|
||||
emac->parent.set_peer_pause_ability = emac_esp32_set_peer_pause_ability;
|
||||
emac->parent.enable_flow_ctrl = emac_esp32_enable_flow_ctrl;
|
||||
emac->parent.transmit = emac_esp32_transmit;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -28,6 +28,7 @@
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_rom_crc.h"
|
||||
|
||||
static const char *TAG = "dm9051.mac";
|
||||
|
||||
@ -38,6 +39,7 @@ static const char *TAG = "dm9051.mac";
|
||||
#define DM9051_RX_MEM_MAX_SIZE (16384)
|
||||
#define DM9051_RX_HDR_SIZE (4)
|
||||
|
||||
#define DM9051_HASH_FILTER_TABLE_SIZE (64)
|
||||
|
||||
typedef struct {
|
||||
uint8_t flag; // 0 = no frame, 1 = frame received, others = possible memory pointer error or tcpip_checksum_offload status flag if enabled
|
||||
@ -69,10 +71,11 @@ typedef struct {
|
||||
int int_gpio_num;
|
||||
esp_timer_handle_t poll_timer;
|
||||
uint32_t poll_period_ms;
|
||||
uint8_t addr[6];
|
||||
uint8_t addr[ETH_ADDR_LEN];
|
||||
bool packets_remain;
|
||||
bool flow_ctrl_enabled;
|
||||
uint8_t *rx_buffer;
|
||||
uint8_t hash_filter_cnt[DM9051_HASH_FILTER_TABLE_SIZE];
|
||||
} emac_dm9051_t;
|
||||
|
||||
static void *dm9051_spi_init(const void *spi_config)
|
||||
@ -232,7 +235,7 @@ static esp_err_t dm9051_memory_read(emac_dm9051_t *emac, uint8_t *buffer, uint32
|
||||
static esp_err_t dm9051_get_mac_addr(emac_dm9051_t *emac)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int i = 0; i < ETH_ADDR_LEN; i++) {
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_PAR + i, &emac->addr[i]), err, TAG, "read PAR failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
@ -246,7 +249,7 @@ err:
|
||||
static esp_err_t dm9051_set_mac_addr(emac_dm9051_t *emac)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int i = 0; i < ETH_ADDR_LEN; i++) {
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_PAR + i, emac->addr[i]), err, TAG, "write PAR failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
@ -269,6 +272,50 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dm9051_hash_filter_modify(emac_dm9051_t *emac, uint8_t *addr, bool add)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
// calculate crc32 value of mac address
|
||||
uint32_t crc = ~esp_rom_crc32_le(0, addr, ETH_ADDR_LEN);
|
||||
|
||||
uint8_t hash_value = crc & 0x3F;
|
||||
uint8_t hash_group = hash_value / 8;
|
||||
uint8_t hash_bit = hash_value % 8;
|
||||
|
||||
uint8_t mar;
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, (DM9051_MAR + hash_group), &mar), err, TAG, "read MAR failed");
|
||||
if (add) {
|
||||
// add address to hash table
|
||||
mar |= (1 << hash_bit);
|
||||
emac->hash_filter_cnt[hash_value]++;
|
||||
} else {
|
||||
emac->hash_filter_cnt[hash_value]--;
|
||||
if (emac->hash_filter_cnt[hash_value] == 0) {
|
||||
// remove address from hash table
|
||||
mar &= ~(1 << hash_bit);
|
||||
}
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, (DM9051_MAR + hash_group), mar), err, TAG, "write MAR failed");
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_add_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
ESP_RETURN_ON_ERROR(dm9051_hash_filter_modify(emac, addr, true), TAG, "modify multicast table failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_rm_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
ESP_RETURN_ON_ERROR(dm9051_hash_filter_modify(emac, addr, false), TAG, "modify multicast table failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief software reset dm9051 internal register
|
||||
*/
|
||||
@ -327,9 +374,9 @@ static esp_err_t dm9051_setup_default(emac_dm9051_t *emac)
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR, 0x00), err, TAG, "write TCR failed");
|
||||
/* Before enabling the RCR feature, make sure to set IMR_PAR in IMR */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_IMR, IMR_PAR), err, TAG, "write DM9051_IMR failed");
|
||||
/* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), receive all multicast packets */
|
||||
/* stop receiving, no promiscuous mode, no runt packet(size < 64bytes) */
|
||||
/* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_ALL_MCAST), err, TAG, "write RCR failed");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC), err, TAG, "write RCR failed");
|
||||
/* retry late collision packet, at most two transmit command can be issued before transmit complete */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP), err, TAG, "write TCR2 failed");
|
||||
/* enable auto transmit */
|
||||
@ -615,6 +662,22 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_set_all_multicast(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
uint8_t rcr = 0;
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_RCR, &rcr), err, TAG, "read RCR failed");
|
||||
if (enable) {
|
||||
rcr |= RCR_ALL_MCAST;
|
||||
} else {
|
||||
rcr &= ~RCR_ALL_MCAST;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCR, rcr), err, TAG, "write RCR failed");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
@ -911,10 +974,13 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
|
||||
emac->parent.set_duplex = emac_dm9051_set_duplex;
|
||||
emac->parent.set_link = emac_dm9051_set_link;
|
||||
emac->parent.set_promiscuous = emac_dm9051_set_promiscuous;
|
||||
emac->parent.set_all_multicast = emac_dm9051_set_all_multicast;
|
||||
emac->parent.set_peer_pause_ability = emac_dm9051_set_peer_pause_ability;
|
||||
emac->parent.enable_flow_ctrl = emac_dm9051_enable_flow_ctrl;
|
||||
emac->parent.transmit = emac_dm9051_transmit;
|
||||
emac->parent.receive = emac_dm9051_receive;
|
||||
emac->parent.add_mac_filter = emac_dm9051_add_mac_filter;
|
||||
emac->parent.rm_mac_filter = emac_dm9051_rm_mac_filter;
|
||||
|
||||
if (dm9051_config->custom_spi_driver.init != NULL && dm9051_config->custom_spi_driver.deinit != NULL
|
||||
&& dm9051_config->custom_spi_driver.read != NULL && dm9051_config->custom_spi_driver.write != NULL) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
@ -21,9 +21,10 @@
|
||||
#include "freertos/semphr.h"
|
||||
#include "ksz8851.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "esp_rom_crc.h"
|
||||
|
||||
#define KSZ8851_ETH_MAC_RX_BUF_SIZE_AUTO (0)
|
||||
#define KSZ8851_HASH_FILTER_TABLE_SIZE (64)
|
||||
|
||||
typedef struct {
|
||||
spi_device_handle_t hdl;
|
||||
@ -49,6 +50,7 @@ typedef struct {
|
||||
uint32_t poll_period_ms;
|
||||
uint8_t *rx_buffer;
|
||||
uint8_t *tx_buffer;
|
||||
uint8_t hash_filter_cnt[KSZ8851_HASH_FILTER_TABLE_SIZE];
|
||||
} emac_ksz8851snl_t;
|
||||
|
||||
typedef struct {
|
||||
@ -309,7 +311,7 @@ static esp_err_t init_set_defaults(emac_ksz8851snl_t *emac)
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDTTR, RXDTTR_INIT_VALUE), err, TAG, "RXDTTR write failed");
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDBCTR, RXDBCTR_INIT_VALUE), err, TAG, "RXDBCTR write failed");
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1,
|
||||
RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXMAFMA | RXCR1_RXAE), err, TAG, "RXCR1 write failed");
|
||||
RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXAE | RXCR1_RXBE), err, TAG, "RXCR1 write failed");
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR2,
|
||||
(4 << RXCR2_SRDBL_SHIFT) | RXCR2_IUFFP | RXCR2_RXIUFCEZ | RXCR2_UDPLFE | RXCR2_RXICMPFCC), err, TAG, "RXCR2 write failed");
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RXFCTE | RXQCR_ADRFE), err, TAG, "RXQCR write failed");
|
||||
@ -603,6 +605,52 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) uint32_t ksz8851_reflect32(uint32_t x) {
|
||||
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
|
||||
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
|
||||
x = ((x >> 4) & 0x0F0F0F0Fu) | ((x & 0x0F0F0F0Fu) << 4);
|
||||
x = ((x >> 8) & 0x00FF00FFu) | ((x & 0x00FF00FFu) << 8);
|
||||
x = (x >> 16) | (x << 16);
|
||||
return x;
|
||||
}
|
||||
|
||||
static esp_err_t ksz8851_hash_filter_modify(emac_ksz8851snl_t *emac, uint8_t *addr, bool add)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
// calculate crc32 value of mac address
|
||||
uint32_t crc = ksz8851_reflect32(~esp_rom_crc32_le(0, addr, ETH_ADDR_LEN));
|
||||
|
||||
uint8_t hash_value = (crc >> 26) & 0x3F;
|
||||
uint8_t hash_group = hash_value / 16;
|
||||
uint8_t hash_bit = hash_value % 16;
|
||||
|
||||
uint16_t mar_reg = KSZ8851_MAHTR0 + hash_group * 2;
|
||||
if (add) {
|
||||
// add address to hash table
|
||||
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, mar_reg, 1 << hash_bit), err, TAG, "hash table set bits failed");
|
||||
emac->hash_filter_cnt[hash_value]++;
|
||||
} else {
|
||||
// remove address from hash table
|
||||
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, mar_reg, 1 << hash_bit), err, TAG, "hash table clear bits failed");
|
||||
emac->hash_filter_cnt[hash_value]--;
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_ksz8851_add_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
|
||||
return ksz8851_hash_filter_modify(emac, addr, true);
|
||||
}
|
||||
|
||||
static esp_err_t emac_ksz8851_rm_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
|
||||
return ksz8851_hash_filter_modify(emac, addr, false);
|
||||
}
|
||||
|
||||
static esp_err_t emac_ksz8851_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -675,15 +723,33 @@ static esp_err_t emac_ksz8851_set_promiscuous(esp_eth_mac_t *mac, bool enable)
|
||||
uint16_t rxcr1;
|
||||
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXCR1, &rxcr1), err, TAG, "RXCR1 read failed");
|
||||
if (enable) {
|
||||
// NOTE(v.chistyakov): set promiscuous mode
|
||||
ESP_LOGD(TAG, "setting promiscuous mode");
|
||||
rxcr1 |= RXCR1_RXAE | RXCR1_RXINVF;
|
||||
rxcr1 &= ~(RXCR1_RXPAFMA | RXCR1_RXMAFMA);
|
||||
} else {
|
||||
// NOTE(v.chistyakov): set hash perfect (default)
|
||||
ESP_LOGD(TAG, "setting hash perfect");
|
||||
rxcr1 |= RXCR1_RXPAFMA;
|
||||
rxcr1 &= ~(RXCR1_RXAE | RXCR1_RXINVF | RXCR1_RXMAFMA);
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_RXCR1, rxcr1), err, TAG, "RXCR1 write failed");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_ksz8851_set_all_multicast(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
|
||||
uint16_t rxcr1;
|
||||
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXCR1, &rxcr1), err, TAG, "RXCR1 read failed");
|
||||
if (enable) {
|
||||
ESP_LOGD(TAG, "setting perfect with multicast passed");
|
||||
rxcr1 |= RXCR1_RXAE| RXCR1_RXPAFMA | RXCR1_RXMAFMA;
|
||||
rxcr1 &= ~RXCR1_RXINVF;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "setting hash perfect");
|
||||
rxcr1 |= RXCR1_RXPAFMA;
|
||||
rxcr1 &= ~(RXCR1_RXAE | RXCR1_RXINVF | RXCR1_RXMAFMA);
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_RXCR1, rxcr1), err, TAG, "RXCR1 write failed");
|
||||
err:
|
||||
@ -855,10 +921,13 @@ esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851
|
||||
emac->parent.write_phy_reg = emac_ksz8851_write_phy_reg;
|
||||
emac->parent.set_addr = emac_ksz8851_set_addr;
|
||||
emac->parent.get_addr = emac_ksz8851_get_addr;
|
||||
emac->parent.add_mac_filter = emac_ksz8851_add_mac_filter;
|
||||
emac->parent.rm_mac_filter = emac_ksz8851_rm_mac_filter;
|
||||
emac->parent.set_speed = emac_ksz8851_set_speed;
|
||||
emac->parent.set_duplex = emac_ksz8851_set_duplex;
|
||||
emac->parent.set_link = emac_ksz8851_set_link;
|
||||
emac->parent.set_promiscuous = emac_ksz8851_set_promiscuous;
|
||||
emac->parent.set_all_multicast = emac_ksz8851_set_all_multicast;
|
||||
emac->parent.enable_flow_ctrl = emac_ksz8851_enable_flow_ctrl;
|
||||
emac->parent.set_peer_pause_ability = emac_ksz8851_set_peer_pause_ability;
|
||||
emac->parent.del = emac_ksz8851_del;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -62,9 +62,10 @@ typedef struct {
|
||||
int int_gpio_num;
|
||||
esp_timer_handle_t poll_timer;
|
||||
uint32_t poll_period_ms;
|
||||
uint8_t addr[6];
|
||||
uint8_t addr[ETH_ADDR_LEN];
|
||||
bool packets_remain;
|
||||
uint8_t *rx_buffer;
|
||||
uint8_t mcast_cnt;
|
||||
} emac_w5500_t;
|
||||
|
||||
static void *w5500_spi_init(const void *spi_config)
|
||||
@ -354,8 +355,8 @@ static esp_err_t w5500_setup_default(emac_w5500_t *emac)
|
||||
/* Disable interrupt for all sockets by default */
|
||||
reg_value = 0;
|
||||
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SIMR, ®_value, sizeof(reg_value)), err, TAG, "write SIMR failed");
|
||||
/* Enable MAC RAW mode for SOCK0, enable MAC filter, no blocking broadcast and multicast */
|
||||
reg_value = W5500_SMR_MAC_RAW | W5500_SMR_MAC_FILTER;
|
||||
/* Enable MAC RAW mode for SOCK0, enable MAC filter, no blocking broadcast and block multicast */
|
||||
reg_value = W5500_SMR_MAC_RAW | W5500_SMR_MAC_FILTER | W5500_SMR_MAC_BLOCK_MCAST;
|
||||
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_MR(0), ®_value, sizeof(reg_value)), err, TAG, "write SMR failed");
|
||||
/* Enable receive event for SOCK0 */
|
||||
reg_value = W5500_SIR_RECV;
|
||||
@ -458,6 +459,60 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_set_block_ip4_mcast(esp_eth_mac_t *mac, bool block)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
|
||||
uint8_t smr;
|
||||
ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)), err, TAG, "read SMR failed");
|
||||
if (block) {
|
||||
smr |= W5500_SMR_MAC_BLOCK_MCAST;
|
||||
} else {
|
||||
smr &= ~W5500_SMR_MAC_BLOCK_MCAST;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)), err, TAG, "write SMR failed");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_add_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
|
||||
// W5500 doesn't have specific MAC filter, so we just un-block multicast. W5500 filters out all multicast packets
|
||||
// except for IP multicast. However, behavior is not consistent. IPv4 multicast can be blocked, but IPv6 is always
|
||||
// accepted (this is not documented behavior, but it's observed on the real hardware).
|
||||
if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e) {
|
||||
ESP_GOTO_ON_ERROR(emac_w5500_set_block_ip4_mcast(mac, false), err, TAG, "set block multicast failed");
|
||||
emac->mcast_cnt++;
|
||||
} else if (addr[0] == 0x33 && addr[1] == 0x33) {
|
||||
ESP_LOGW(TAG, "IPv6 multicast is always filtered in by W5500.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "W5500 filters in IP multicast frames only!");
|
||||
ret = ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_del_mac_filter(esp_eth_mac_t *mac, uint8_t *addr)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
|
||||
|
||||
ESP_GOTO_ON_FALSE(!(addr[0] == 0x33 && addr[1] == 0x33), ESP_FAIL, err, TAG, "IPv6 multicast is always filtered in by W5500.");
|
||||
|
||||
if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e && emac->mcast_cnt > 0) {
|
||||
emac->mcast_cnt--;
|
||||
}
|
||||
if (emac->mcast_cnt == 0) {
|
||||
// W5500 doesn't have specific MAC filter, so we just block multicast
|
||||
ESP_GOTO_ON_ERROR(emac_w5500_set_block_ip4_mcast(mac, true), err, TAG, "set block multicast failed");
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_set_link(esp_eth_mac_t *mac, eth_link_t link)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -543,6 +598,19 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_set_all_multicast(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
|
||||
ESP_RETURN_ON_ERROR(emac_w5500_set_block_ip4_mcast(mac, !enable), TAG, "set block multicast failed");
|
||||
emac->mcast_cnt = 0;
|
||||
if (enable) {
|
||||
ESP_LOGW(TAG, "W5500 filters in IP multicast frames only!");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "W5500 always filters in IPv6 multicast frames!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t emac_w5500_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
|
||||
{
|
||||
/* w5500 doesn't support flow control function, so accept any value */
|
||||
@ -888,10 +956,13 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con
|
||||
emac->parent.read_phy_reg = emac_w5500_read_phy_reg;
|
||||
emac->parent.set_addr = emac_w5500_set_addr;
|
||||
emac->parent.get_addr = emac_w5500_get_addr;
|
||||
emac->parent.add_mac_filter = emac_w5500_add_mac_filter;
|
||||
emac->parent.rm_mac_filter = emac_w5500_del_mac_filter;
|
||||
emac->parent.set_speed = emac_w5500_set_speed;
|
||||
emac->parent.set_duplex = emac_w5500_set_duplex;
|
||||
emac->parent.set_link = emac_w5500_set_link;
|
||||
emac->parent.set_promiscuous = emac_w5500_set_promiscuous;
|
||||
emac->parent.set_all_multicast = emac_w5500_set_all_multicast;
|
||||
emac->parent.set_peer_pause_ability = emac_w5500_set_peer_pause_ability;
|
||||
emac->parent.enable_flow_ctrl = emac_w5500_enable_flow_ctrl;
|
||||
emac->parent.transmit = emac_w5500_transmit;
|
||||
|
@ -61,6 +61,7 @@
|
||||
|
||||
#define W5500_SMR_MAC_RAW (1<<2) // MAC RAW mode
|
||||
#define W5500_SMR_MAC_FILTER (1<<7) // MAC filter
|
||||
#define W5500_SMR_MAC_BLOCK_MCAST (1<<5) // Block multicast
|
||||
|
||||
#define W5500_SCR_OPEN (0x01) // Open command
|
||||
#define W5500_SCR_CLOSE (0x10) // Close command
|
||||
|
@ -565,6 +565,135 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
|
||||
vSemaphoreDelete(mutex);
|
||||
}
|
||||
|
||||
TEST_CASE("internal emac address filter", "[esp_emac]")
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
EventGroupHandle_t eth_event_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_group != NULL);
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
|
||||
esp_eth_mac_t *mac = mac_init(NULL, NULL);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
esp_eth_phy_t *phy = phy_init(NULL);
|
||||
TEST_ASSERT_NOT_NULL(phy);
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
|
||||
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
|
||||
TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); // install driver
|
||||
TEST_ASSERT_NOT_NULL(eth_handle);
|
||||
extra_eth_config(eth_handle);
|
||||
// ---------------------------------------
|
||||
// Loopback greatly simplifies the test !!
|
||||
// ---------------------------------------
|
||||
bool loopback_en = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
|
||||
|
||||
// start Ethernet driver
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
// wait for connection start
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
|
||||
// wait for connection establish
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
emac_hal_context_t emac_hal;
|
||||
emac_hal_init(&emac_hal);
|
||||
|
||||
// Test destination address filter functions
|
||||
uint8_t test_mac_addr1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
|
||||
uint8_t test_mac_addr2[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
|
||||
uint8_t test_mac_addr3[] = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26};
|
||||
uint8_t mac_got[ETH_ADDR_LEN];
|
||||
|
||||
// Test adding filter with specific address number
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter(&emac_hal, test_mac_addr1, 1));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 1));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr1, mac_got, ETH_ADDR_LEN);
|
||||
|
||||
// Test auto adding filter
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter_auto(&emac_hal, test_mac_addr2));
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter_auto(&emac_hal, test_mac_addr3));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 2));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr2, mac_got, ETH_ADDR_LEN);
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 3));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr3, mac_got, ETH_ADDR_LEN);
|
||||
|
||||
// Test removing filter with specific address number
|
||||
TEST_ESP_OK(emac_hal_rm_addr_da_filter(&emac_hal, test_mac_addr1, 1));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, emac_hal_get_addr_da_filter(&emac_hal, mac_got, 1));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 2));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr2, mac_got, ETH_ADDR_LEN);
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 3));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr3, mac_got, ETH_ADDR_LEN);
|
||||
|
||||
// Test auto removing filter
|
||||
TEST_ESP_OK(emac_hal_rm_addr_da_filter_auto(&emac_hal, test_mac_addr3));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, emac_hal_get_addr_da_filter(&emac_hal, mac_got, 3));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 2));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr2, mac_got, ETH_ADDR_LEN);
|
||||
TEST_ESP_OK(emac_hal_rm_addr_da_filter_auto(&emac_hal, test_mac_addr2));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, emac_hal_get_addr_da_filter(&emac_hal, mac_got, 2));
|
||||
|
||||
// Test clear all filters
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter(&emac_hal, test_mac_addr1, 1));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, 1));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr1, mac_got, ETH_ADDR_LEN);
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter(&emac_hal, test_mac_addr2, EMAC_LL_MAX_MAC_ADDR_NUM));
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, EMAC_LL_MAX_MAC_ADDR_NUM));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr2, mac_got, ETH_ADDR_LEN);
|
||||
emac_hal_clear_addr_da_filters(&emac_hal);
|
||||
for(uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, emac_hal_get_addr_da_filter(&emac_hal, mac_got, i));
|
||||
}
|
||||
|
||||
// Test auto adding filter with all addresses
|
||||
uint8_t test_mac_addr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x00};
|
||||
for(uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
test_mac_addr[5]++;
|
||||
TEST_ESP_OK(emac_hal_add_addr_da_filter_auto(&emac_hal, test_mac_addr));
|
||||
}
|
||||
TEST_ASSERT_EQUAL(ESP_FAIL, emac_hal_add_addr_da_filter_auto(&emac_hal, test_mac_addr));
|
||||
test_mac_addr[5] = 0x00;
|
||||
for(uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
test_mac_addr[5]++;
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, mac_got, i));
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(test_mac_addr, mac_got, ETH_ADDR_LEN);
|
||||
}
|
||||
|
||||
// Test invalid arguments
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_add_addr_da_filter(&emac_hal, NULL, 1));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_add_addr_da_filter(&emac_hal, test_mac_addr1, 0));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_add_addr_da_filter(&emac_hal, test_mac_addr1, EMAC_LL_MAX_MAC_ADDR_NUM + 1));
|
||||
|
||||
TEST_ESP_OK(emac_hal_get_addr_da_filter(&emac_hal, NULL, 1));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_get_addr_da_filter(&emac_hal, test_mac_addr1, 0));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_get_addr_da_filter(&emac_hal, test_mac_addr1, EMAC_LL_MAX_MAC_ADDR_NUM + 1));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_rm_addr_da_filter(&emac_hal, NULL, 1));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_rm_addr_da_filter(&emac_hal, test_mac_addr1, 0));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_rm_addr_da_filter(&emac_hal, test_mac_addr1, EMAC_LL_MAX_MAC_ADDR_NUM + 1));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, emac_hal_rm_addr_da_filter_auto(&emac_hal, NULL));
|
||||
|
||||
// Check EMAC_LL_MAX_MAC_ADDR_NUM is correctly defined (e.i. max MAC address was really limited to this value during coreConsultant phase)
|
||||
emac_ll_add_addr_filter(emac_hal.mac_regs, EMAC_LL_MAX_MAC_ADDR_NUM + 1, test_mac_addr1, 0, false);
|
||||
TEST_ASSERT_FALSE(emac_ll_get_addr_filter(emac_hal.mac_regs, EMAC_LL_MAX_MAC_ADDR_NUM + 1, mac_got, NULL, NULL));
|
||||
|
||||
// stop Ethernet driver
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
// wait for connection stop
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
|
||||
TEST_ESP_OK(phy->del(phy));
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
extra_cleanup();
|
||||
vEventGroupDelete(eth_event_group);
|
||||
}
|
||||
|
||||
TEST_CASE("internal emac ref rmii clk out", "[esp_emac_clk_out]")
|
||||
{
|
||||
esp_eth_mac_t *mac = mac_init(NULL, NULL);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -211,9 +211,6 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = true;
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
|
||||
@ -222,6 +219,19 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Enable receive all multicast\n");
|
||||
// ---------------------------------------
|
||||
EventBits_t expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
int expected_multicast_rx_cnt = 2;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
bool all_multicast = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_ALL_MULTICAST, &all_multicast));
|
||||
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
|
||||
EventBits_t bits = 0;
|
||||
@ -232,11 +242,130 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(5000));
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
|
||||
(ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Disable receive all multicast\n");
|
||||
// ---------------------------------------
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
|
||||
// It's not documented behavior, but it's observed on the real hardware.
|
||||
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
#else
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 0;
|
||||
#endif
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
all_multicast = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_ALL_MULTICAST, &all_multicast));
|
||||
// send POKE to indicate that the DUT reconfigured the filter
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Add multicast addresses to the filter\n");
|
||||
// ---------------------------------------
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 2;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
uint8_t multicast_addr_ip4[ETH_ADDR_LEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, multicast_addr_ip4));
|
||||
uint8_t multicast_addr_ip6[ETH_ADDR_LEN] = {0x33, 0x33, 0x00, 0x00, 0x00, 0x00};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, multicast_addr_ip6));
|
||||
// send POKE to indicate that the DUT reconfigured the filter
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// --------------------------------------------
|
||||
printf("Remove one multicast address from the filter\n");
|
||||
// --------------------------------------------
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip4));
|
||||
// send POKE to indicate that the DUT reconfigured the filter
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// ----------------------------------------------
|
||||
printf("Remove all multicast addresses from the filter\n");
|
||||
// ----------------------------------------------
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
|
||||
// It's not documented behavior, but it's observed on the real hardware.
|
||||
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
#else
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 0;
|
||||
#endif
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets and hence filter delete fails.
|
||||
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
|
||||
TEST_ESP_ERR(ESP_FAIL, esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip6));
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip6));
|
||||
#endif
|
||||
// send POKE to indicate that the DUT reconfigured the filter
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
@ -254,7 +383,7 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]"
|
||||
// *** SPI Ethernet modules deviation ***
|
||||
// Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
|
||||
// occupy all the resources under heavy Rx traffic and it would not be possible to access
|
||||
// the Ethernet module to stop it. Therfore, the Rx task priority is set lower than "test" task
|
||||
// the Ethernet module to stop it. Therefore, the Rx task priority is set lower than "test" task
|
||||
// to be able to be preempted.
|
||||
#if CONFIG_TARGET_USE_SPI_ETHERNET
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
@ -511,3 +640,73 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
|
||||
vEventGroupDelete(eth_event_rx_group);
|
||||
vEventGroupDelete(eth_event_state_group);
|
||||
}
|
||||
|
||||
#define FORMAT_MAC(mac_addr, a, b, c, d, e, f) do { mac_addr[0] = a; mac_addr[1] = b; mac_addr[2] = c; mac_addr[3] = d; mac_addr[4] = e; mac_addr[5] = f; } while(0)
|
||||
TEST_CASE("w5500_multicast_filter", "[ethernet_l2]")
|
||||
{
|
||||
esp_eth_mac_t *mac = mac_init(NULL, NULL);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
esp_eth_phy_t *phy = phy_init(NULL);
|
||||
TEST_ASSERT_NOT_NULL(phy);
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
|
||||
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
|
||||
TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); // install driver
|
||||
TEST_ASSERT_NOT_NULL(eth_handle);
|
||||
extra_eth_config(eth_handle);
|
||||
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_state_group != NULL);
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_state_group));
|
||||
EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_rx_group != NULL);
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = false;
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
|
||||
// test app will parse the DUT MAC from this line of log output
|
||||
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: W5500 SPI Ethernet module does not support internal loopback
|
||||
#if !CONFIG_TARGET_ETH_PHY_DEVICE_W5500
|
||||
// ---------------------------------------
|
||||
// Loopback greatly simplifies the test !!
|
||||
// ---------------------------------------
|
||||
bool loopback_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
#endif
|
||||
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
|
||||
bool all_multicast = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_ALL_MULTICAST, &all_multicast));
|
||||
|
||||
EventBits_t bits = 0;
|
||||
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_MULTICAST_RECV_BIT);
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_MULTICAST_RECV_BIT, true, true, pdMS_TO_TICKS(500));
|
||||
TEST_ASSERT((bits & ETH_MULTICAST_RECV_BIT) == ETH_MULTICAST_RECV_BIT);
|
||||
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
|
||||
phy->del(phy);
|
||||
mac->del(mac);
|
||||
extra_cleanup();
|
||||
vEventGroupDelete(eth_event_rx_group);
|
||||
vEventGroupDelete(eth_event_state_group);
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import contextlib
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
from multiprocessing import connection
|
||||
from multiprocessing import Pipe
|
||||
from multiprocessing import Process
|
||||
from multiprocessing import connection
|
||||
from typing import Iterator
|
||||
|
||||
import pytest
|
||||
@ -169,14 +169,16 @@ def ethernet_l2_test(dut: IdfDut) -> None:
|
||||
r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})'
|
||||
)
|
||||
dut_mac = res.group(1).decode('utf-8')
|
||||
# wait for POKE msg to be sure the switch already started forwarding the port's traffic
|
||||
# (there might be slight delay due to the RSTP execution)
|
||||
target_if.recv_resp_poke(mac=dut_mac)
|
||||
target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame
|
||||
target_if.send_eth_packet(
|
||||
'01:00:5e:00:00:00'
|
||||
) # IPv4 multicast frame (some SPI Eth modules filter multicast other than IP)
|
||||
target_if.send_eth_packet(mac=dut_mac) # unicast frame
|
||||
for _ in range(5):
|
||||
# wait for POKE msg to be sure the switch already started forwarding the port's traffic
|
||||
# (there might be slight delay due to the RSTP execution)
|
||||
# or wait for next POKE msg to be sure the DUT reconfigured the filter
|
||||
target_if.recv_resp_poke(mac=dut_mac)
|
||||
target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame
|
||||
target_if.send_eth_packet('01:00:5e:00:00:00') # IPv4 multicast frame
|
||||
target_if.send_eth_packet('33:33:00:00:00:00') # IPv6 multicast frame
|
||||
target_if.send_eth_packet(mac=dut_mac) # unicast frame
|
||||
|
||||
dut.expect_unity_test_output(extra_before=res.group(1))
|
||||
|
||||
dut.expect_exact("Enter next test, or 'enter' to see menu")
|
||||
|
@ -244,6 +244,7 @@ struct esp_netif_driver_ifconfig {
|
||||
esp_err_t (*transmit)(void *h, void *buffer, size_t len); /*!< transmit function pointer */
|
||||
esp_err_t (*transmit_wrap)(void *h, void *buffer, size_t len, void *netstack_buffer); /*!< transmit wrap function pointer */
|
||||
void (*driver_free_rx_buffer)(void *h, void* buffer); /*!< free rx buffer function pointer */
|
||||
esp_err_t (*driver_set_mac_filter)(void *h, const uint8_t *mac, size_t mac_len, bool add); /*!< set mac filter function pointer */
|
||||
};
|
||||
|
||||
typedef struct esp_netif_driver_ifconfig esp_netif_driver_ifconfig_t;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -605,6 +605,58 @@ esp_err_t esp_netif_deinit(void)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IGMP
|
||||
static err_t netif_igmp_mac_filter_cb(struct netif *netif, const ip4_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
esp_netif_t *esp_netif;
|
||||
if (netif == NULL || (esp_netif = lwip_get_esp_netif(netif)) == NULL) {
|
||||
// internal pointer hasn't been configured yet (probably in the interface init_fn())
|
||||
return ERR_VAL;
|
||||
}
|
||||
ESP_LOGD(TAG, "Multicast add filter IPv4: " IPSTR, IP2STR(group));
|
||||
uint8_t mac[NETIF_MAX_HWADDR_LEN];
|
||||
mac[0] = 0x01;
|
||||
mac[1] = 0x00;
|
||||
mac[2] = 0x5E;
|
||||
mac[3] = (group->addr >> 8) & 0x7F; // Only use lower 7 bits
|
||||
mac[4] = (group->addr >> 16) & 0xFF;
|
||||
mac[5] = (group->addr >> 24) & 0xFF;
|
||||
|
||||
bool add = action == NETIF_ADD_MAC_FILTER ? true : false;
|
||||
if (esp_netif->driver_set_mac_filter(esp_netif->driver_handle, mac, sizeof(mac), add) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to %s multicast filter for IPv4", add ? "add" : "remove");
|
||||
return ERR_VAL;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IGMP */
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_MLD
|
||||
static err_t netif_mld_mac_filter_cb(struct netif *netif, const ip6_addr_t *group, enum netif_mac_filter_action action)
|
||||
{
|
||||
esp_netif_t *esp_netif;
|
||||
if (netif == NULL || (esp_netif = lwip_get_esp_netif(netif)) == NULL) {
|
||||
// internal pointer hasn't been configured yet (probably in the interface init_fn())
|
||||
return ERR_VAL;
|
||||
}
|
||||
ESP_LOGD(TAG, "Multicast add filter IPv6: " IPV6STR, IPV62STR(*group));
|
||||
uint8_t mac[NETIF_MAX_HWADDR_LEN];
|
||||
mac[0] = 0x33;
|
||||
mac[1] = 0x33;
|
||||
mac[2] = group->addr[3] & 0xFF;
|
||||
mac[3] = (group->addr[3] >> 8) & 0xFF;
|
||||
mac[4] = (group->addr[3] >> 16) & 0xFF;
|
||||
mac[5] = (group->addr[3] >> 24) & 0xFF;
|
||||
|
||||
bool add = action == NETIF_ADD_MAC_FILTER ? true : false;
|
||||
if (esp_netif->driver_set_mac_filter(esp_netif->driver_handle, mac, sizeof(mac), add) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to %s multicast filter for IPv6", add ? "add" : "remove");
|
||||
return ERR_VAL;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
|
||||
|
||||
static esp_err_t esp_netif_init_configuration(esp_netif_t *esp_netif, const esp_netif_config_t *cfg)
|
||||
{
|
||||
// Basic esp_netif and lwip is a mandatory configuration and cannot be updated after esp_netif_new()
|
||||
@ -694,6 +746,11 @@ static esp_err_t esp_netif_init_configuration(esp_netif_t *esp_netif, const esp_
|
||||
if (esp_netif_driver_config->driver_free_rx_buffer) {
|
||||
esp_netif->driver_free_rx_buffer = esp_netif_driver_config->driver_free_rx_buffer;
|
||||
}
|
||||
#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
|
||||
if (esp_netif_driver_config->driver_set_mac_filter) {
|
||||
esp_netif->driver_set_mac_filter = esp_netif_driver_config->driver_set_mac_filter;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@ -931,6 +988,14 @@ static esp_err_t esp_netif_lwip_add(esp_netif_t *esp_netif)
|
||||
#if CONFIG_ESP_NETIF_BRIDGE_EN
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_BRIDGE_EN
|
||||
if (esp_netif->driver_set_mac_filter) {
|
||||
#if LWIP_IPV4 && LWIP_IGMP
|
||||
netif_set_igmp_mac_filter(esp_netif->lwip_netif, netif_igmp_mac_filter_cb);
|
||||
#endif
|
||||
#if LWIP_IPV6 && LWIP_IPV6_MLD
|
||||
netif_set_mld_mac_filter(esp_netif->lwip_netif, netif_mld_mac_filter_cb);
|
||||
#endif
|
||||
}
|
||||
lwip_set_esp_netif(esp_netif->lwip_netif, esp_netif);
|
||||
return ESP_OK;
|
||||
}
|
||||
@ -1001,6 +1066,9 @@ esp_err_t esp_netif_set_driver_config(esp_netif_t *esp_netif,
|
||||
esp_netif->driver_transmit = driver_config->transmit;
|
||||
esp_netif->driver_transmit_wrap = driver_config->transmit_wrap;
|
||||
esp_netif->driver_free_rx_buffer = driver_config->driver_free_rx_buffer;
|
||||
#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)
|
||||
esp_netif->driver_set_mac_filter = driver_config->driver_set_mac_filter;
|
||||
#endif /* LWIP_IPV4 && LWIP_IGMP */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ struct esp_netif_obj {
|
||||
esp_err_t (*driver_transmit)(void *h, void *buffer, size_t len);
|
||||
esp_err_t (*driver_transmit_wrap)(void *h, void *buffer, size_t len, void *pbuf);
|
||||
void (*driver_free_rx_buffer)(void *h, void* buffer);
|
||||
esp_err_t (*driver_set_mac_filter)(void *h, const uint8_t *mac, size_t mac_len, bool add);
|
||||
|
||||
// dhcp related
|
||||
esp_netif_dhcp_status_t dhcpc_status;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -241,7 +241,7 @@ static void ethernet_deinit(test_vfs_eth_network_t *network_hndls)
|
||||
static test_vfs_eth_tap_msg_t s_test_msg = {
|
||||
.header = {
|
||||
.src.addr = {0},
|
||||
.dest.addr = { 0x01, 0x00, 0x00, 0x00, 0xBE, 0xEF },
|
||||
.dest.addr = {0},
|
||||
.type = ETH_FILTER_BE,
|
||||
},
|
||||
.str = "This is ESP32 L2 TAP test msg"
|
||||
@ -256,7 +256,10 @@ static void send_task(void *task_param)
|
||||
send_task_control_t *send_control = (send_task_control_t *)task_param;
|
||||
test_vfs_eth_network_t *eth_network_hndls = send_control->eth_network_hndls_p;
|
||||
|
||||
// Set test message source MAC address to the MAC of the Ethernet interface
|
||||
esp_eth_ioctl(eth_network_hndls->eth_handle, ETH_CMD_G_MAC_ADDR, &s_test_msg.header.src.addr);
|
||||
// Set test message destination MAC address to the MAC of the Ethernet interface to not be filtered out in loopback mode
|
||||
esp_eth_ioctl(eth_network_hndls->eth_handle, ETH_CMD_G_MAC_ADDR, &s_test_msg.header.dest.addr);
|
||||
|
||||
if (send_control->eth_type >= 0 && send_control->eth_type <= 0xFFFF) {
|
||||
s_test_msg.header.type = htons(send_control->eth_type);
|
||||
@ -693,14 +696,11 @@ TEST_CASE("esp32 l2tap - write", "[ethernet]")
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Verify the write is not successful when use different Ethernet type than the fd is configured to...");
|
||||
test_vfs_eth_tap_msg_t test_msg = {
|
||||
.header = {
|
||||
.src.addr = {0},
|
||||
.dest.addr = { 0x01, 0x00, 0x00, 0x00, 0xBE, 0xEF },
|
||||
.type = 0,
|
||||
}
|
||||
};
|
||||
|
||||
test_vfs_eth_tap_msg_t test_msg;
|
||||
// Set test message source MAC address to the MAC of the Ethernet interface
|
||||
esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &test_msg.header.src.addr);
|
||||
// Set test message destination MAC address to the MAC of the Ethernet interface to not be filtered out in loopback mode
|
||||
esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &test_msg.header.dest.addr);
|
||||
// set different Ethernet type than the fd is configured to
|
||||
test_msg.header.type = htons(ETH_FILTER_LE + 10);
|
||||
TEST_ASSERT_EQUAL(-1, write(eth_tap_fd, &test_msg, sizeof(test_msg)));
|
||||
@ -723,6 +723,7 @@ typedef struct {
|
||||
uint16_t task_id;
|
||||
uint16_t eth_filter;
|
||||
SemaphoreHandle_t semaphore;
|
||||
test_vfs_eth_network_t *eth_network_hndls_p;
|
||||
} task_info_t;
|
||||
|
||||
static void multi_fds_task (void *task_param)
|
||||
@ -732,13 +733,11 @@ static void multi_fds_task (void *task_param)
|
||||
|
||||
int eth_tap_fds[NUM_OF_FDS];
|
||||
test_vfs_eth_tap_msg_t recv_msg;
|
||||
test_vfs_eth_tap_msg_t test_msg = {
|
||||
.header = {
|
||||
.src.addr = {0},
|
||||
.dest.addr = { 0x01, 0x00, 0x00, 0x00, 0xBE, 0xEF },
|
||||
.type = 0,
|
||||
}
|
||||
};
|
||||
test_vfs_eth_tap_msg_t test_msg;
|
||||
// Set test message source MAC address to the MAC of the Ethernet interface
|
||||
esp_eth_ioctl(task_info->eth_network_hndls_p->eth_handle, ETH_CMD_G_MAC_ADDR, &test_msg.header.src.addr);
|
||||
// Set test message destination MAC address to the MAC of the Ethernet interface to not be filtered out in loopback mode
|
||||
esp_eth_ioctl(task_info->eth_network_hndls_p->eth_handle, ETH_CMD_G_MAC_ADDR, &test_msg.header.dest.addr);
|
||||
|
||||
for (int i = 0; i < sizeof(eth_tap_fds) / sizeof(int); i++) {
|
||||
eth_tap_fds[i] = open("/dev/net/tap", O_NONBLOCK);
|
||||
@ -805,6 +804,7 @@ TEST_CASE("esp32 l2tap - read/write multiple fd's used by multiple tasks", "[eth
|
||||
task_info[i].task_id = i;
|
||||
task_info[i].eth_filter = 0x750A + i * 100;
|
||||
task_info[i].semaphore = xSemaphoreCreateBinary();
|
||||
task_info[i].eth_network_hndls_p = ð_network_hndls;
|
||||
}
|
||||
|
||||
xTaskCreate(multi_fds_task, "multi_fds_task_1", 4096, &task_info[0], tskIDLE_PRIORITY + 2, NULL);
|
||||
@ -865,9 +865,12 @@ TEST_CASE("esp32 l2tap - time stamping", "[ethernet]")
|
||||
.timestamp = 0,
|
||||
}
|
||||
};
|
||||
uint16_t exp_sequence_id = test_ptp_msg.ptp_msg.ptp_hdr.sequence_id;
|
||||
// Add PTP multicast destination MAC address to the filter
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_ADD_MAC_FILTER, test_ptp_msg.eth_hdr.dest.addr));
|
||||
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_network_hndls.eth_handle, ETH_CMD_G_MAC_ADDR, &test_ptp_msg.eth_hdr.src.addr));
|
||||
|
||||
uint16_t exp_sequence_id = test_ptp_msg.ptp_msg.ptp_hdr.sequence_id;
|
||||
// wrap "Info Records Buffer" into union to ensure proper alignment of data (this is typically needed when
|
||||
// accessing double word variables or structs containing double word variables)
|
||||
union {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -103,9 +103,8 @@ void emac_hal_init_mac_default(emac_hal_context_t *hal)
|
||||
emac_ll_sa_inverse_filter_enable(hal->mac_regs, false);
|
||||
/* MAC blocks all control frames */
|
||||
emac_ll_set_pass_ctrl_frame_mode(hal->mac_regs, EMAC_LL_CONTROL_FRAME_BLOCKALL);
|
||||
/* AFM module passes all received broadcast frames and multicast frames */
|
||||
/* AFM module passes all received broadcast frames */
|
||||
emac_ll_broadcast_frame_enable(hal->mac_regs, true);
|
||||
emac_ll_pass_all_multicast_enable(hal->mac_regs, true);
|
||||
/* Address Check block operates in normal filtering mode for the DA address */
|
||||
emac_ll_da_inverse_filter_enable(hal->mac_regs, false);
|
||||
/* Disable Promiscuous Mode */
|
||||
@ -202,6 +201,82 @@ void emac_hal_set_address(emac_hal_context_t *hal, uint8_t *mac_addr)
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t emac_hal_add_addr_da_filter(emac_hal_context_t *hal, const uint8_t *mac_addr, uint8_t addr_num)
|
||||
{
|
||||
if (addr_num < 1 || addr_num > EMAC_LL_MAX_MAC_ADDR_NUM || mac_addr == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
emac_ll_add_addr_filter(hal->mac_regs, addr_num, mac_addr, 0, false);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t emac_hal_get_addr_da_filter(emac_hal_context_t *hal, uint8_t *mac_addr, uint8_t addr_num)
|
||||
{
|
||||
if (addr_num < 1 || addr_num > EMAC_LL_MAX_MAC_ADDR_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
bool is_source = false;
|
||||
if ((emac_ll_get_addr_filter(hal->mac_regs, addr_num, mac_addr, NULL, &is_source) != true) || is_source) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t emac_hal_add_addr_da_filter_auto(emac_hal_context_t *hal, uint8_t *mac_addr)
|
||||
{
|
||||
for (uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
// find the first free address filter
|
||||
if (emac_ll_get_addr_filter(hal->mac_regs, i, NULL, NULL, NULL) == false) {
|
||||
emac_hal_add_addr_da_filter(hal, mac_addr, i);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t emac_hal_rm_addr_da_filter(emac_hal_context_t *hal, const uint8_t *mac_addr, uint8_t addr_num)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (mac_addr == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t addr_got[6];
|
||||
if ((ret = emac_hal_get_addr_da_filter(hal, addr_got, addr_num)) == ESP_OK) {
|
||||
if (memcmp(addr_got, mac_addr, 6) == 0) {
|
||||
emac_ll_rm_addr_filter(hal->mac_regs, addr_num);
|
||||
} else {
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t emac_hal_rm_addr_da_filter_auto(emac_hal_context_t *hal, const uint8_t *mac_addr)
|
||||
{
|
||||
if (mac_addr == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint8_t addr_got[6];
|
||||
for (uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
if (emac_hal_get_addr_da_filter(hal, addr_got, i) == ESP_OK) {
|
||||
if (memcmp(addr_got, mac_addr, 6) == 0) {
|
||||
emac_ll_rm_addr_filter(hal->mac_regs, i);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void emac_hal_clear_addr_da_filters(emac_hal_context_t *hal)
|
||||
{
|
||||
for (uint8_t i = 1; i <= EMAC_LL_MAX_MAC_ADDR_NUM; i++) {
|
||||
if (emac_hal_get_addr_da_filter(hal, NULL, i) == ESP_OK) {
|
||||
emac_ll_rm_addr_filter(hal->mac_regs, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SOC_EMAC_IEEE1588V2_SUPPORTED
|
||||
static inline uint32_t subsecond2nanosecond(emac_hal_context_t *hal, uint32_t subsecond)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -130,6 +130,9 @@ extern "C" {
|
||||
/* Enable needed interrupts (recv/recv_buf_unavailabal/normal must be enabled to make eth work) */
|
||||
#define EMAC_LL_CONFIG_ENABLE_INTR_MASK (EMAC_LL_INTR_RECEIVE_ENABLE | EMAC_LL_INTR_NORMAL_SUMMARY_ENABLE)
|
||||
|
||||
/* Maximum number of MAC address to be filtered */
|
||||
#define EMAC_LL_MAX_MAC_ADDR_NUM 8
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for the EMAC module
|
||||
*
|
||||
@ -371,6 +374,11 @@ static inline uint32_t emac_ll_receive_read_ctrl_state(emac_mac_dev_t *mac_regs)
|
||||
return mac_regs->emacdebug.mtlrfrcs;
|
||||
}
|
||||
|
||||
static inline uint32_t emac_ll_read_debug_reg(emac_mac_dev_t *mac_regs)
|
||||
{
|
||||
return mac_regs->emacdebug.val;
|
||||
}
|
||||
|
||||
/* emacmiidata */
|
||||
static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data)
|
||||
{
|
||||
@ -388,6 +396,50 @@ static inline void emac_ll_set_addr(emac_mac_dev_t *mac_regs, const uint8_t *add
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr0high, address0_hi, (addr[5] << 8) | addr[4]);
|
||||
mac_regs->emacaddr0low = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0]);
|
||||
}
|
||||
|
||||
/* emacaddrN */
|
||||
static inline void emac_ll_add_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, const uint8_t *mac_addr, uint8_t mask, bool filter_for_source)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, (mac_addr[5] << 8) | mac_addr[4]);
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control = mask;
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.source_address = filter_for_source;
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 1;
|
||||
mac_regs->emacaddr[addr_num].emacaddrlow = (mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | (mac_addr[0]);
|
||||
}
|
||||
|
||||
static inline bool emac_ll_get_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, uint8_t *mac_addr, uint8_t *mask, bool *filter_for_source)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
if (mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable) {
|
||||
if (mac_addr != NULL) {
|
||||
*(&mac_addr[0]) = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
|
||||
*(&mac_addr[1]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
|
||||
*(&mac_addr[2]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
|
||||
*(&mac_addr[3]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
|
||||
*(&mac_addr[4]) = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
|
||||
*(&mac_addr[5]) = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
|
||||
}
|
||||
if (mask != NULL) {
|
||||
*mask = mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control;
|
||||
}
|
||||
if (filter_for_source != NULL) {
|
||||
*filter_for_source = mac_regs->emacaddr[addr_num].emacaddrhigh.source_address;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void emac_ll_rm_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 0;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, 0);
|
||||
mac_regs->emacaddr[addr_num].emacaddrlow = 0;
|
||||
}
|
||||
|
||||
/*************** End of mac regs operation *********************/
|
||||
|
||||
/************** Start of dma regs operation ********************/
|
||||
@ -548,12 +600,17 @@ static inline void emac_ll_disable_all_intr(emac_dma_dev_t *dma_regs)
|
||||
|
||||
static inline void emac_ll_enable_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t mask)
|
||||
{
|
||||
dma_regs->dmain_en.val |= mask;
|
||||
uint32_t temp_mask = dma_regs->dmain_en.val;
|
||||
temp_mask |= mask;
|
||||
dma_regs->dmain_en.val = temp_mask;
|
||||
|
||||
}
|
||||
|
||||
static inline void emac_ll_disable_corresponding_intr(emac_dma_dev_t *dma_regs, uint32_t mask)
|
||||
{
|
||||
dma_regs->dmain_en.val &= ~mask;
|
||||
uint32_t temp_mask = dma_regs->dmain_en.val;
|
||||
temp_mask &= ~mask;
|
||||
dma_regs->dmain_en.val = temp_mask;
|
||||
}
|
||||
|
||||
static inline uint32_t emac_ll_get_intr_enable_status(emac_dma_dev_t *dma_regs)
|
||||
|
@ -147,6 +147,9 @@ extern "C" {
|
||||
/* Enable needed MAC interrupts */
|
||||
#define EMAC_LL_CONFIG_ENABLE_MAC_INTR_MASK (EMAC_LL_MAC_INTR_TIME_STAMP_ENABLE)
|
||||
|
||||
/* Maximum number of MAC address to be filtered */
|
||||
#define EMAC_LL_MAX_MAC_ADDR_NUM 8
|
||||
|
||||
/************** Start of mac regs operation ********************/
|
||||
/* emacgmiiaddr */
|
||||
static inline void emac_ll_set_csr_clock_division(emac_mac_dev_t *mac_regs, uint32_t div_mode)
|
||||
@ -376,6 +379,49 @@ static inline void emac_ll_set_addr(emac_mac_dev_t *mac_regs, const uint8_t *add
|
||||
mac_regs->emacaddr0low = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | (addr[0]);
|
||||
}
|
||||
|
||||
/* emacaddrN */
|
||||
static inline void emac_ll_add_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, const uint8_t *mac_addr, uint8_t mask, bool filter_for_source)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, (mac_addr[5] << 8) | mac_addr[4]);
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control = mask;
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.source_address = filter_for_source;
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 1;
|
||||
mac_regs->emacaddr[addr_num].emacaddrlow = (mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | (mac_addr[0]);
|
||||
}
|
||||
|
||||
static inline bool emac_ll_get_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num, uint8_t *mac_addr, uint8_t *mask, bool *filter_for_source)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
if (mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable) {
|
||||
if (mac_addr != NULL) {
|
||||
*(&mac_addr[0]) = mac_regs->emacaddr[addr_num].emacaddrlow & 0xFF;
|
||||
*(&mac_addr[1]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 8) & 0xFF;
|
||||
*(&mac_addr[2]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 16) & 0xFF;
|
||||
*(&mac_addr[3]) = (mac_regs->emacaddr[addr_num].emacaddrlow >> 24) & 0xFF;
|
||||
*(&mac_addr[4]) = mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi & 0xFF;
|
||||
*(&mac_addr[5]) = (mac_regs->emacaddr[addr_num].emacaddrhigh.mac_address_hi >> 8) & 0xFF;
|
||||
}
|
||||
if (mask != NULL) {
|
||||
*mask = mac_regs->emacaddr[addr_num].emacaddrhigh.mask_byte_control;
|
||||
}
|
||||
if (filter_for_source != NULL) {
|
||||
*filter_for_source = mac_regs->emacaddr[addr_num].emacaddrhigh.source_address;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void emac_ll_rm_addr_filter(emac_mac_dev_t *mac_regs, uint8_t addr_num)
|
||||
{
|
||||
addr_num = addr_num - 1; // MAC Address1 is located at emacaddr[0]
|
||||
mac_regs->emacaddr[addr_num].emacaddrhigh.address_enable = 0;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(mac_regs->emacaddr[addr_num].emacaddrhigh, mac_address_hi, 0);
|
||||
mac_regs->emacaddr[addr_num].emacaddrlow = 0;
|
||||
}
|
||||
|
||||
/* emacintmask */
|
||||
static inline void emac_ll_enable_corresponding_emac_intr(emac_mac_dev_t *mac_regs, uint32_t mask)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -258,6 +258,8 @@ void emac_hal_init_dma_default(emac_hal_context_t *hal, emac_hal_dma_config_t *h
|
||||
|
||||
#define emac_hal_set_promiscuous(hal, enable) emac_ll_promiscuous_mode_enable((hal)->mac_regs, enable)
|
||||
|
||||
#define emac_hal_pass_all_multicast_enable(hal, enable) emac_ll_pass_all_multicast_enable((hal)->mac_regs, enable)
|
||||
|
||||
/**
|
||||
* @brief Send MAC-CTRL frames to peer (EtherType=0x8808, opcode=0x0001, dest_addr=MAC-specific-ctrl-proto-01 (01:80:c2:00:00:01))
|
||||
*/
|
||||
@ -273,6 +275,17 @@ void emac_hal_set_phy_cmd(emac_hal_context_t *hal, uint32_t phy_addr, uint32_t p
|
||||
|
||||
void emac_hal_set_address(emac_hal_context_t *hal, uint8_t *mac_addr);
|
||||
|
||||
esp_err_t emac_hal_add_addr_da_filter(emac_hal_context_t *hal, const uint8_t *mac_addr, uint8_t addr_num);
|
||||
|
||||
esp_err_t emac_hal_get_addr_da_filter(emac_hal_context_t *hal, uint8_t *mac_addr, uint8_t addr_num);
|
||||
|
||||
esp_err_t emac_hal_add_addr_da_filter_auto(emac_hal_context_t *hal, uint8_t *mac_addr);
|
||||
|
||||
esp_err_t emac_hal_rm_addr_da_filter(emac_hal_context_t *hal, const uint8_t *mac_addr, uint8_t addr_num);
|
||||
|
||||
esp_err_t emac_hal_rm_addr_da_filter_auto(emac_hal_context_t *hal, const uint8_t *mac_addr);
|
||||
|
||||
void emac_hal_clear_addr_da_filters(emac_hal_context_t *hal);
|
||||
/**
|
||||
* @brief Starts EMAC Transmission & Reception
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -11,6 +11,20 @@ extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the eighth 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control : 6; /*These bits are mask control bits for comparison of each of the EMACADDR7 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR7 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR7 High [15:8]. Bit[28]: EMACADDR7 High [7:0]. Bit[27]: EMACADDR7 Low [31:24]. Bit[24]: EMACADDR7 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address : 1; /*When this bit is set the EMACADDR7[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR7[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable : 1; /*When this bit is set the address filter module uses the eighth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddrhigh;
|
||||
uint32_t emacaddrlow; /*This field contains the lower 32 bits of the eighth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
} emac_mac_addr_t;
|
||||
|
||||
typedef struct emac_mac_dev_s {
|
||||
volatile union {
|
||||
struct {
|
||||
@ -207,101 +221,8 @@ typedef struct emac_mac_dev_s {
|
||||
uint32_t val;
|
||||
} emacaddr0high;
|
||||
uint32_t emacaddr0low; /*This field contains the lower 32 bits of the first 6-byte MAC address. This is used by the MAC for filtering the received frames and inserting the MAC address in the Transmit Flow Control (Pause) Frames.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address1_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the second 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control : 6; /*These bits are mask control bits for comparison of each of the EMACADDR1 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR1 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR1 High [15:8]. Bit[28]: EMACADDR1 High [7:0]. Bit[27]: EMACADDR1 Low [31:24]. Bit[24]: EMACADDR1 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address : 1; /*When this bit is set the EMACADDR1[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR1[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable1 : 1; /*When this bit is set the address filter module uses the second MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr1high;
|
||||
uint32_t emacaddr1low; /*This field contains the lower 32 bits of the second 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address2_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the third 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control2 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR2 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR2 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR2 High [15:8]. Bit[28]: EMACADDR2 High [7:0]. Bit[27]: EMACADDR2 Low [31:24]. Bit[24]: EMACADDR2 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address2 : 1; /*When this bit is set the EMACADDR2[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR2[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable2 : 1; /*When this bit is set the address filter module uses the third MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr2high;
|
||||
uint32_t emacaddr2low; /*This field contains the lower 32 bits of the third 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address3_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fourth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control3 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR3 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR3 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR3 High [15:8]. Bit[28]: EMACADDR3 High [7:0]. Bit[27]: EMACADDR3 Low [31:24]. Bit[24]: EMACADDR3 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address3 : 1; /*When this bit is set the EMACADDR3[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR3[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable3 : 1; /*When this bit is set the address filter module uses the fourth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr3high;
|
||||
uint32_t emacaddr3low; /*This field contains the lower 32 bits of the fourth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address4_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fifth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control4 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR4 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR4 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR4 High [15:8]. Bit[28]: EMACADDR4 High [7:0]. Bit[27]: EMACADDR4 Low [31:24]. Bit[24]: EMACADDR4 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address4 : 1; /*When this bit is set the EMACADDR4[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR4[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable4 : 1; /*When this bit is set the address filter module uses the fifth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr4high;
|
||||
uint32_t emacaddr4low; /*This field contains the lower 32 bits of the fifth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address5_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the sixth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control5 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR5 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR5 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR5 High [15:8]. Bit[28]: EMACADDR5 High [7:0]. Bit[27]: EMACADDR5 Low [31:24]. Bit[24]: EMACADDR5 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address5 : 1; /*When this bit is set the EMACADDR5[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR5[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable5 : 1; /*When this bit is set the address filter module uses the sixth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr5high;
|
||||
uint32_t emacaddr5low; /*This field contains the lower 32 bits of the sixth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address6_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the seventh 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control6 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR6 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR6 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR6 High [15:8]. Bit[28]: EMACADDR6 High [7:0]. Bit[27]: EMACADDR6 Low [31:24]. Bit[24]: EMACADDR6 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address6 : 1; /*When this bit is set the EMACADDR6[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR6[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable6 : 1; /*When this bit is set the address filter module uses the seventh MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr6high;
|
||||
uint32_t emacaddr6low; /*This field contains the lower 32 bits of the seventh 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address7_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the eighth 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control7 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR7 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR7 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR7 High [15:8]. Bit[28]: EMACADDR7 High [7:0]. Bit[27]: EMACADDR7 Low [31:24]. Bit[24]: EMACADDR7 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address7 : 1; /*When this bit is set the EMACADDR7[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR7[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable7 : 1; /*When this bit is set the address filter module uses the eighth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr7high;
|
||||
uint32_t emacaddr7low; /*This field contains the lower 32 bits of the eighth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
uint32_t reserved_1080;
|
||||
uint32_t reserved_1084;
|
||||
uint32_t reserved_1088;
|
||||
uint32_t reserved_108c;
|
||||
uint32_t reserved_1090;
|
||||
uint32_t reserved_1094;
|
||||
uint32_t reserved_1098;
|
||||
uint32_t reserved_109c;
|
||||
uint32_t reserved_10a0;
|
||||
uint32_t reserved_10a4;
|
||||
uint32_t reserved_10a8;
|
||||
uint32_t reserved_10ac;
|
||||
uint32_t reserved_10b0;
|
||||
uint32_t reserved_10b4;
|
||||
uint32_t reserved_10b8;
|
||||
uint32_t reserved_10bc;
|
||||
uint32_t reserved_10c0;
|
||||
uint32_t reserved_10c4;
|
||||
emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/
|
||||
uint32_t reserved_10c4; // AN control register
|
||||
uint32_t reserved_10c8;
|
||||
uint32_t reserved_10cc;
|
||||
uint32_t reserved_10d0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -11,6 +11,20 @@ extern "C" {
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the eighth 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control : 6; /*These bits are mask control bits for comparison of each of the EMAC_ADDR bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMAC_ADDR registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMAC_ADDR High [15:8]. Bit[28]: EMAC_ADDR High [7:0]. Bit[27]: EMAC_ADDR Low [31:24]. Bit[24]: EMAC_ADDR Low [7:0]. You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address : 1; /*When this bit is set the EMAC_ADDR[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMAC_ADDR[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable : 1; /*When this bit is set the address filter module uses the eighth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddrhigh;
|
||||
uint32_t emacaddrlow; /*This field contains the lower 32 bits of the eighth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
} emac_mac_addr_t;
|
||||
|
||||
typedef struct emac_mac_dev_s {
|
||||
volatile union {
|
||||
struct {
|
||||
@ -207,101 +221,8 @@ typedef struct emac_mac_dev_s {
|
||||
uint32_t val;
|
||||
} emacaddr0high;
|
||||
uint32_t emacaddr0low; /*This field contains the lower 32 bits of the first 6-byte MAC address. This is used by the MAC for filtering the received frames and inserting the MAC address in the Transmit Flow Control (Pause) Frames.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address1_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the second 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control : 6; /*These bits are mask control bits for comparison of each of the EMACADDR1 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR1 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR1 High [15:8]. Bit[28]: EMACADDR1 High [7:0]. Bit[27]: EMACADDR1 Low [31:24]. Bit[24]: EMACADDR1 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address : 1; /*When this bit is set the EMACADDR1[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR1[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable1 : 1; /*When this bit is set the address filter module uses the second MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr1high;
|
||||
uint32_t emacaddr1low; /*This field contains the lower 32 bits of the second 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address2_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the third 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control2 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR2 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR2 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR2 High [15:8]. Bit[28]: EMACADDR2 High [7:0]. Bit[27]: EMACADDR2 Low [31:24]. Bit[24]: EMACADDR2 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address2 : 1; /*When this bit is set the EMACADDR2[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR2[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable2 : 1; /*When this bit is set the address filter module uses the third MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr2high;
|
||||
uint32_t emacaddr2low; /*This field contains the lower 32 bits of the third 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address3_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fourth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control3 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR3 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR3 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR3 High [15:8]. Bit[28]: EMACADDR3 High [7:0]. Bit[27]: EMACADDR3 Low [31:24]. Bit[24]: EMACADDR3 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address3 : 1; /*When this bit is set the EMACADDR3[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR3[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable3 : 1; /*When this bit is set the address filter module uses the fourth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr3high;
|
||||
uint32_t emacaddr3low; /*This field contains the lower 32 bits of the fourth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address4_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the fifth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control4 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR4 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR4 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR4 High [15:8]. Bit[28]: EMACADDR4 High [7:0]. Bit[27]: EMACADDR4 Low [31:24]. Bit[24]: EMACADDR4 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address4 : 1; /*When this bit is set the EMACADDR4[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR4[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable4 : 1; /*When this bit is set the address filter module uses the fifth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr4high;
|
||||
uint32_t emacaddr4low; /*This field contains the lower 32 bits of the fifth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address5_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the sixth 6-byte MAC address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control5 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR5 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR5 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR5 High [15:8]. Bit[28]: EMACADDR5 High [7:0]. Bit[27]: EMACADDR5 Low [31:24]. Bit[24]: EMACADDR5 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address5 : 1; /*When this bit is set the EMACADDR5[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR5[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable5 : 1; /*When this bit is set the address filter module uses the sixth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr5high;
|
||||
uint32_t emacaddr5low; /*This field contains the lower 32 bits of the sixth 6-byte MAC address. The content of this field is undefined so the register needs to be configured after the initialization process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address6_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the seventh 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control6 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR6 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR6 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR6 High [15:8]. Bit[28]: EMACADDR6 High [7:0]. Bit[27]: EMACADDR6 Low [31:24]. Bit[24]: EMACADDR6 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address6 : 1; /*When this bit is set the EMACADDR6[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR6[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable6 : 1; /*When this bit is set the address filter module uses the seventh MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr6high;
|
||||
uint32_t emacaddr6low; /*This field contains the lower 32 bits of the seventh 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
volatile union {
|
||||
struct {
|
||||
uint32_t mac_address7_hi : 16; /*This field contains the upper 16 bits Bits[47:32] of the eighth 6-byte MAC Address.*/
|
||||
uint32_t reserved16 : 8;
|
||||
uint32_t mask_byte_control7 : 6; /*These bits are mask control bits for comparison of each of the EMACADDR7 bytes. When set high the MAC does not compare the corresponding byte of received DA or SA with the contents of EMACADDR7 registers. Each bit controls the masking of the bytes as follows: Bit[29]: EMACADDR7 High [15:8]. Bit[28]: EMACADDR7 High [7:0]. Bit[27]: EMACADDR7 Low [31:24]. Bit[24]: EMACADDR7 Low [7:0].You can filter a group of addresses (known as group address filtering) by masking one or more bytes of the address.*/
|
||||
uint32_t source_address7 : 1; /*When this bit is set the EMACADDR7[47:0] is used to compare with the SA fields of the received frame. When this bit is reset the EMACADDR7[47:0] is used to compare with the DA fields of the received frame.*/
|
||||
uint32_t address_enable7 : 1; /*When this bit is set the address filter module uses the eighth MAC address for perfect filtering. When this bit is reset the address filter module ignores the address for filtering.*/
|
||||
};
|
||||
uint32_t val;
|
||||
} emacaddr7high;
|
||||
uint32_t emacaddr7low; /*This field contains the lower 32 bits of the eighth 6-byte MAC address.The content of this field is undefined so the register needs to be configured after the initialization Process.*/
|
||||
uint32_t reserved_1080;
|
||||
uint32_t reserved_1084;
|
||||
uint32_t reserved_1088;
|
||||
uint32_t reserved_108c;
|
||||
uint32_t reserved_1090;
|
||||
uint32_t reserved_1094;
|
||||
uint32_t reserved_1098;
|
||||
uint32_t reserved_109c;
|
||||
uint32_t reserved_10a0;
|
||||
uint32_t reserved_10a4;
|
||||
uint32_t reserved_10a8;
|
||||
uint32_t reserved_10ac;
|
||||
uint32_t reserved_10b0;
|
||||
uint32_t reserved_10b4;
|
||||
uint32_t reserved_10b8;
|
||||
uint32_t reserved_10bc;
|
||||
uint32_t reserved_10c0;
|
||||
uint32_t reserved_10c4;
|
||||
emac_mac_addr_t emacaddr[15]; /*Offset: 0x40-0xC0. MAC Address1-15 registers. Each MAC address register contains the high and low 32-bit fields for MAC addresses 1-15.*/
|
||||
uint32_t reserved_10c4; // AN control register
|
||||
uint32_t reserved_10c8;
|
||||
uint32_t reserved_10cc;
|
||||
uint32_t reserved_10d0;
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
@ -83,6 +83,11 @@
|
||||
|
||||
#define ETH_TYPE_PTP 0x88F7
|
||||
|
||||
#define SET_MAC_ADDR(addr, a, b, c, d, e, f) do { \
|
||||
addr[0] = a; addr[1] = b; addr[2] = c; \
|
||||
addr[3] = d; addr[4] = e; addr[5] = f; \
|
||||
} while(0)
|
||||
|
||||
#define ERROR ESP_FAIL
|
||||
#define OK ESP_OK
|
||||
|
||||
@ -651,6 +656,13 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state,
|
||||
// get HW address
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, &state->intf_hw_addr);
|
||||
|
||||
// Add well-known PTP multicast destination MAC addresses to the filter
|
||||
uint8_t dest_addr[ETH_ADDR_LEN];
|
||||
SET_MAC_ADDR(dest_addr, 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, dest_addr);
|
||||
SET_MAC_ADDR(dest_addr, 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, dest_addr);
|
||||
|
||||
state->remote_time_ns_prev = 0;
|
||||
state->local_time_ns_prev = 0;
|
||||
|
||||
@ -845,6 +857,19 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state,
|
||||
static int ptp_destroy_state(FAR struct ptp_state_s *state)
|
||||
{
|
||||
#ifdef ESP_PTP
|
||||
// Remove well-known PTP multicast destination MAC addresses from the filter
|
||||
esp_eth_handle_t eth_handle;
|
||||
if (ioctl(state->ptp_socket, L2TAP_G_DEVICE_DRV_HNDL, ð_handle) < 0)
|
||||
{
|
||||
ptperr("failed to get socket eth_handle %d\n", errno);
|
||||
return ERROR;
|
||||
}
|
||||
uint8_t dest_addr[ETH_ADDR_LEN];
|
||||
SET_MAC_ADDR(dest_addr, 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, dest_addr);
|
||||
SET_MAC_ADDR(dest_addr, 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, dest_addr);
|
||||
|
||||
if (state->ptp_socket > 0)
|
||||
{
|
||||
close(state->ptp_socket);
|
||||
|
@ -252,6 +252,11 @@ examples/protocols/sockets/tcp_client:
|
||||
- if: SOC_WIFI_SUPPORTED != 1
|
||||
# linux target won't work with CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN=y
|
||||
|
||||
examples/protocols/sockets/udp_multicast:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32p4"]
|
||||
|
||||
examples/protocols/static_ip:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
|
166
examples/protocols/sockets/udp_multicast/pytest_udp_multicast.py
Normal file
166
examples/protocols/sockets/udp_multicast/pytest_udp_multicast.py
Normal file
@ -0,0 +1,166 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import ipaddress
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
from typing import Any
|
||||
|
||||
import netifaces
|
||||
import pytest
|
||||
from common_test_methods import get_host_ip4_by_dest_ip
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
PORT = 3333
|
||||
IPV6_REGEX = (
|
||||
r'(?<![0-9a-fA-F:])('
|
||||
r'(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,7}:)|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2})|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3})|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4})|'
|
||||
r'(([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5})|'
|
||||
r'([0-9a-fA-F]{1,4}:)((:[0-9a-fA-F]{1,4}){1,6})|'
|
||||
r'(:)((:[0-9a-fA-F]{1,4}){1,7}|:)'
|
||||
r'))(?![0-9a-fA-F:])[^\w]'
|
||||
)
|
||||
|
||||
|
||||
def find_target_if(my_if: str = '') -> str:
|
||||
# try to determine which interface to use
|
||||
netifs = os.listdir('/sys/class/net/')
|
||||
# order matters - ETH NIC with the highest number is connected to DUT on CI runner
|
||||
netifs.sort(reverse=True)
|
||||
logging.info('detected interfaces: %s', str(netifs))
|
||||
|
||||
for netif in netifs:
|
||||
# if no interface defined, try to find it automatically
|
||||
if my_if == '':
|
||||
if netif.find('eth') == 0 or netif.find('enp') == 0 or netif.find('eno') == 0:
|
||||
return netif
|
||||
else:
|
||||
if netif.find(my_if) == 0:
|
||||
return my_if
|
||||
|
||||
raise RuntimeError('network interface not found')
|
||||
|
||||
|
||||
def get_link_local_for_interface(if_name: str) -> Any:
|
||||
addrs = netifaces.ifaddresses(if_name)
|
||||
if netifaces.AF_INET6 in addrs:
|
||||
for addr in addrs[netifaces.AF_INET6]:
|
||||
if addr['addr'].startswith('fe80:'):
|
||||
return addr['addr'].split('%')[0]
|
||||
raise RuntimeError(f'No link-local address found on interface {if_name}')
|
||||
|
||||
|
||||
def test_examples_udp_multicast_proto(dut: Dut, ip_version: str = 'ipv4', nic: str = '') -> None:
|
||||
"""
|
||||
Test UDP multicast functionality for both IPv4 and IPv6.
|
||||
|
||||
Args:
|
||||
dut: Device under test
|
||||
ip_version: 'ipv4', ipv4_mapped or 'ipv6'
|
||||
"""
|
||||
# Create multicast socket based on IP version
|
||||
if ip_version == 'ipv4' or ip_version == 'ipv4_mapped':
|
||||
# IPv4 socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
|
||||
|
||||
# Get ESP32's IPv4 address and multicast address
|
||||
ip_addr = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode()
|
||||
multicast_addr = dut.expect(r'Configured IPV4 Multicast address (\d+\.\d+\.\d+\.\d+)[^\d]')[1].decode()
|
||||
|
||||
# Get host interface IP to bind to
|
||||
host_ip = get_host_ip4_by_dest_ip(ip_addr)
|
||||
|
||||
# Bind to all interfaces
|
||||
sock.bind(('0.0.0.0', PORT))
|
||||
|
||||
# Set socket options for multicast
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
|
||||
# Specify the outgoing interface for multicast
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host_ip))
|
||||
|
||||
# Join multicast group
|
||||
sock.setsockopt(
|
||||
socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(multicast_addr) + socket.inet_aton(host_ip)
|
||||
)
|
||||
|
||||
elif ip_version == 'ipv6':
|
||||
net_if = find_target_if(nic)
|
||||
|
||||
# IPv6 socket
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 32)
|
||||
|
||||
# Get ESP32's IPv6 address and multicast address
|
||||
ip_addr = dut.expect(rf'IPv6 address: {IPV6_REGEX}', timeout=30)[1].decode()
|
||||
ip_addr = str(ipaddress.IPv6Address(ip_addr))
|
||||
multicast_addr = dut.expect(rf'Configured IPV6 Multicast address {IPV6_REGEX}')[1].decode()
|
||||
|
||||
# Bind to all interfaces
|
||||
sock.bind(('::', PORT))
|
||||
|
||||
# Set socket options for multicast
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
|
||||
# Specify the outgoing interface for multicast
|
||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, socket.if_nametoindex(net_if))
|
||||
|
||||
# Get host interface IPv6
|
||||
host_ip = get_link_local_for_interface(net_if)
|
||||
|
||||
# Join multicast group
|
||||
mreq = struct.pack('16si', socket.inet_pton(socket.AF_INET6, multicast_addr), socket.if_nametoindex(net_if))
|
||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
|
||||
else:
|
||||
raise RuntimeError(f'Invalid IP version: {ip_version}')
|
||||
|
||||
# Set socket timeout
|
||||
sock.settimeout(5.0)
|
||||
|
||||
if ip_version == 'ipv4_mapped':
|
||||
dut.expect(r'Sending to IPV6 \(V4 mapped\) multicast address', timeout=5)
|
||||
else:
|
||||
dut.expect(f'Sending to {ip_version.upper()} multicast address', timeout=5)
|
||||
|
||||
try:
|
||||
data, recv_addr = sock.recvfrom(1024)
|
||||
logging.info(f'Received {len(data)} bytes from {recv_addr}')
|
||||
except socket.timeout:
|
||||
raise RuntimeError(f'Timeout waiting for {ip_version} multicast message from ESP32')
|
||||
|
||||
# Check if received from expected source
|
||||
if recv_addr[0] != ip_addr or recv_addr[1] != PORT:
|
||||
raise RuntimeError(f'Received {ip_version} multicast message from unexpected source')
|
||||
|
||||
# Send multicast message
|
||||
message = '!!! Multicast test message from host !!!'.encode()
|
||||
logging.info(f'Sending {ip_version} multicast message to {multicast_addr}:{PORT}')
|
||||
sock.sendto(message, (multicast_addr, PORT))
|
||||
if ip_version == 'ipv4_mapped':
|
||||
dut.expect(f'received {len(message)} bytes from ::FFFF:{host_ip}', timeout=1)
|
||||
else:
|
||||
dut.expect(f'received {len(message)} bytes from {host_ip.upper()}', timeout=1)
|
||||
|
||||
sock.close()
|
||||
|
||||
|
||||
@pytest.mark.eth_ip101
|
||||
@idf_parametrize(
|
||||
'target',
|
||||
['esp32', 'esp32p4'],
|
||||
indirect=['target'],
|
||||
)
|
||||
def test_examples_udp_multicast(dut: Dut) -> None:
|
||||
test_examples_udp_multicast_proto(dut, 'ipv4_mapped')
|
||||
dut.serial.hard_reset()
|
||||
test_examples_udp_multicast_proto(dut, 'ipv6')
|
@ -0,0 +1,14 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
|
||||
CONFIG_EXAMPLE_IPV4_V6=y
|
||||
CONFIG_EXAMPLE_LOOPBACK=n
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
@ -0,0 +1,14 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
|
||||
CONFIG_EXAMPLE_IPV4_V6=y
|
||||
CONFIG_EXAMPLE_LOOPBACK=n
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=31
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=52
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=51
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
Reference in New Issue
Block a user