Merge branch 'feature/ksz8863rll_support' into 'master'
ESP IDF infrastructure preparation for ksz8863rll support See merge request espressif/esp-idf!17203
This commit is contained in:
@@ -169,4 +169,12 @@ menu "Ethernet"
|
||||
help
|
||||
Number of DMA transmit buffers, each buffer is 1600 bytes.
|
||||
endif # ETH_USE_OPENETH
|
||||
|
||||
config ETH_TRANSMIT_MUTEX
|
||||
depends on ETH_ENABLED
|
||||
bool "Enable Transmit Mutex"
|
||||
default n
|
||||
help
|
||||
Prevents multiple accesses when Ethernet interface is used as shared resource and multiple
|
||||
functionalities might try to access it at a time.
|
||||
endmenu
|
||||
|
||||
@@ -126,19 +126,22 @@ typedef struct {
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
ETH_CMD_G_MAC_ADDR, /*!< Get MAC address */
|
||||
ETH_CMD_S_MAC_ADDR, /*!< Set MAC address */
|
||||
ETH_CMD_G_PHY_ADDR, /*!< Get PHY address */
|
||||
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
|
||||
ETH_CMD_G_AUTONEGO, /*!< Get PHY Auto Negotiation */
|
||||
ETH_CMD_S_AUTONEGO, /*!< Set PHY Auto Negotiation */
|
||||
ETH_CMD_G_SPEED, /*!< Get Speed */
|
||||
ETH_CMD_S_SPEED, /*!< Set Speed */
|
||||
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
|
||||
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
|
||||
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
|
||||
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
|
||||
ETH_CMD_S_PHY_LOOPBACK,/*!< Set PHY loopback */
|
||||
ETH_CMD_G_MAC_ADDR, /*!< Get MAC address */
|
||||
ETH_CMD_S_MAC_ADDR, /*!< Set MAC address */
|
||||
ETH_CMD_G_PHY_ADDR, /*!< Get PHY address */
|
||||
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
|
||||
ETH_CMD_G_AUTONEGO, /*!< Get PHY Auto Negotiation */
|
||||
ETH_CMD_S_AUTONEGO, /*!< Set PHY Auto Negotiation */
|
||||
ETH_CMD_G_SPEED, /*!< Get Speed */
|
||||
ETH_CMD_S_SPEED, /*!< Set Speed */
|
||||
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
|
||||
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
|
||||
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
|
||||
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
|
||||
ETH_CMD_S_PHY_LOOPBACK, /*!< Set PHY loopback */
|
||||
|
||||
ETH_CMD_CUSTOM_MAC_CMDS = 0x0FFF, // Offset for start of MAC custom commands
|
||||
ETH_CMD_CUSTOM_PHY_CMDS = 0x1FFF, // Offset for start of PHY custom commands
|
||||
} esp_eth_io_cmd_t;
|
||||
|
||||
/**
|
||||
@@ -245,10 +248,26 @@ esp_err_t esp_eth_update_input_path(
|
||||
* @return
|
||||
* - ESP_OK: transmit frame buffer successfully
|
||||
* - ESP_ERR_INVALID_ARG: transmit frame buffer failed because of some invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: invalid driver state (e.i. driver is not started)
|
||||
* - ESP_ERR_TIMEOUT: transmit frame buffer failed because HW was not get available in predefined period
|
||||
* - ESP_FAIL: transmit frame buffer failed because some other error occurred
|
||||
*/
|
||||
esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Special Transmit with variable number of arguments
|
||||
*
|
||||
* @param[in] hdl handle of Ethernet driver
|
||||
* @param[in] argc number variable arguments
|
||||
* @param ... variable arguments
|
||||
* @return
|
||||
* - ESP_OK: transmit successfull
|
||||
* - ESP_ERR_INVALID_STATE: invalid driver state (e.i. driver is not started)
|
||||
* - ESP_ERR_TIMEOUT: transmit frame buffer failed because HW was not get available in predefined period
|
||||
* - ESP_FAIL: transmit frame buffer failed because some other error occurred
|
||||
*/
|
||||
esp_err_t esp_eth_transmit_vargs(esp_eth_handle_t hdl, uint32_t argc, ...);
|
||||
|
||||
/**
|
||||
* @brief General Receive is deprecated and shall not be accessed from app code,
|
||||
* as polling is not supported by Ethernet.
|
||||
@@ -285,7 +304,7 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
|
||||
* - ESP_FAIL: process io command failed because some other error occurred
|
||||
* - ESP_ERR_NOT_SUPPORTED: requested feature is not supported
|
||||
*
|
||||
* The following IO control commands are supported:
|
||||
* The following common IO control commands are supported:
|
||||
* @li @c ETH_CMD_S_MAC_ADDR sets Ethernet interface MAC address. @c data argument is pointer to MAC address buffer with expected size of 6 bytes.
|
||||
* @li @c ETH_CMD_G_MAC_ADDR gets Ethernet interface MAC address. @c data argument is pointer to a buffer to which MAC address is to be copied. The buffer size must be at least 6 bytes.
|
||||
* @li @c ETH_CMD_S_PHY_ADDR sets PHY address in range of <0-31>. @c data argument is pointer to memory of uint32_t datatype from where the configuration option is read.
|
||||
@@ -303,6 +322,7 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
|
||||
* @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 Note that additional control commands may be available for specific MAC or PHY chips. Please consult specific MAC or PHY documentation or driver code.
|
||||
*/
|
||||
esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data);
|
||||
|
||||
|
||||
@@ -95,13 +95,34 @@ struct esp_eth_mac_s {
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: transmit packet successfully
|
||||
* - ESP_ERR_INVALID_ARG: transmit packet failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: transmit packet failed because of wrong state of MAC
|
||||
* - ESP_ERR_INVALID_SIZE: number of actually sent bytes differs to expected
|
||||
* - ESP_FAIL: transmit packet failed because some other error occurred
|
||||
*
|
||||
* @note Returned error codes may differ for each specific MAC chip.
|
||||
*
|
||||
*/
|
||||
esp_err_t (*transmit)(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Transmit packet from Ethernet MAC constructed with special parameters at Layer2.
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] argc: number variable arguments
|
||||
* @param[in] args: variable arguments
|
||||
*
|
||||
* @note Typical intended use case is to make possible to construct a frame from multiple higher layer
|
||||
* buffers without a need of buffer reallocations. However, other use cases are not limited.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: transmit packet successfully
|
||||
* - ESP_ERR_INVALID_SIZE: number of actually sent bytes differs to expected
|
||||
* - ESP_FAIL: transmit packet failed because some other error occurred
|
||||
*
|
||||
* @note Returned error codes may differ for each specific MAC chip.
|
||||
*
|
||||
*/
|
||||
esp_err_t (*transmit_vargs)(esp_eth_mac_t *mac, uint32_t argc, va_list args);
|
||||
|
||||
/**
|
||||
* @brief Receive packet from Ethernet MAC
|
||||
*
|
||||
@@ -266,6 +287,23 @@ struct esp_eth_mac_s {
|
||||
*/
|
||||
esp_err_t (*set_peer_pause_ability)(esp_eth_mac_t *mac, uint32_t ability);
|
||||
|
||||
/**
|
||||
* @brief Custom IO function of MAC driver. This function is intended to extend common options of esp_eth_ioctl to cover specifics of MAC chip.
|
||||
*
|
||||
* @note This function may not be assigned when the MAC chip supports only most common set of configuration options.
|
||||
*
|
||||
* @param[in] mac: Ethernet MAC instance
|
||||
* @param[in] cmd: IO control command
|
||||
* @param[in, out] data: address of data for `set` command or address where to store the data when used with `get` command
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: process io command successfully
|
||||
* - ESP_ERR_INVALID_ARG: process io command failed because of some invalid argument
|
||||
* - ESP_FAIL: process io command failed because some other error occurred
|
||||
* - ESP_ERR_NOT_SUPPORTED: requested feature is not supported
|
||||
*/
|
||||
esp_err_t (*custom_ioctl)(esp_eth_mac_t *mac, uint32_t cmd, void *data);
|
||||
|
||||
/**
|
||||
* @brief Free memory of Ethernet MAC
|
||||
*
|
||||
@@ -357,7 +395,6 @@ typedef union {
|
||||
} rmii; /*!< EMAC RMII Clock Configuration */
|
||||
} eth_mac_clock_config_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Configuration of Ethernet MAC object
|
||||
*
|
||||
|
||||
@@ -225,6 +225,23 @@ struct esp_eth_phy_s {
|
||||
*/
|
||||
esp_err_t (*set_duplex)(esp_eth_phy_t *phy, eth_duplex_t duplex);
|
||||
|
||||
/**
|
||||
* @brief Custom IO function of PHY driver. This function is intended to extend common options of esp_eth_ioctl to cover specifics of PHY chip.
|
||||
*
|
||||
* @note This function may not be assigned when the PHY chip supports only most common set of configuration options.
|
||||
*
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
* @param[in] cmd: IO control command
|
||||
* @param[in, out] data: address of data for `set` command or address where to store the data when used with `get` command
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: process io command successfully
|
||||
* - ESP_ERR_INVALID_ARG: process io command failed because of some invalid argument
|
||||
* - ESP_FAIL: process io command failed because some other error occurred
|
||||
* - ESP_ERR_NOT_SUPPORTED: requested feature is not supported
|
||||
*/
|
||||
esp_err_t (*custom_ioctl)(esp_eth_phy_t *phy, uint32_t cmd, void *data);
|
||||
|
||||
/**
|
||||
* @brief Free memory of Ethernet PHY instance
|
||||
*
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
/**
|
||||
* @brief Transmit timeout when multiple accesses to network driver
|
||||
*/
|
||||
#define ESP_ETH_TX_TIMEOUT_MS 250
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp_eth";
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ETH_EVENT);
|
||||
@@ -47,6 +54,9 @@ typedef struct {
|
||||
atomic_int ref_count;
|
||||
void *priv;
|
||||
_Atomic esp_eth_fsm_t fsm;
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
SemaphoreHandle_t transmit_mutex;
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
esp_err_t (*stack_input)(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv);
|
||||
esp_err_t (*on_lowlevel_init_done)(esp_eth_handle_t eth_handle);
|
||||
esp_err_t (*on_lowlevel_deinit_done)(esp_eth_handle_t eth_handle);
|
||||
@@ -187,6 +197,10 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
|
||||
.skip_unhandled_events = true
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_timer_create(&check_link_timer_args, ð_driver->check_link_timer), err, TAG, "create link timer failed");
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
eth_driver->transmit_mutex = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(eth_driver->transmit_mutex, ESP_ERR_NO_MEM, err, TAG, "Failed to create transmit mutex");
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
atomic_init(ð_driver->ref_count, 1);
|
||||
atomic_init(ð_driver->fsm, ESP_ETH_FSM_STOP);
|
||||
eth_driver->mac = mac;
|
||||
@@ -225,6 +239,11 @@ err:
|
||||
if (eth_driver->check_link_timer) {
|
||||
esp_timer_delete(eth_driver->check_link_timer);
|
||||
}
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
if (eth_driver->transmit_mutex) {
|
||||
vSemaphoreDelete(eth_driver->transmit_mutex);
|
||||
}
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
free(eth_driver);
|
||||
}
|
||||
return ret;
|
||||
@@ -246,6 +265,9 @@ esp_err_t esp_eth_driver_uninstall(esp_eth_handle_t hdl)
|
||||
esp_eth_mac_t *mac = eth_driver->mac;
|
||||
esp_eth_phy_t *phy = eth_driver->phy;
|
||||
ESP_GOTO_ON_ERROR(esp_timer_delete(eth_driver->check_link_timer), err, TAG, "delete link timer failed");
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
vSemaphoreDelete(eth_driver->transmit_mutex);
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
ESP_GOTO_ON_ERROR(phy->deinit(phy), err, TAG, "deinit phy failed");
|
||||
ESP_GOTO_ON_ERROR(mac->deinit(mac), err, TAG, "deinit mac failed");
|
||||
free(eth_driver);
|
||||
@@ -324,7 +346,44 @@ esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, size_t length)
|
||||
ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "buf length can't be zero");
|
||||
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
|
||||
esp_eth_mac_t *mac = eth_driver->mac;
|
||||
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
if (xSemaphoreTake(eth_driver->transmit_mutex, pdMS_TO_TICKS(ESP_ETH_TX_TIMEOUT_MS)) == pdFALSE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
ret = mac->transmit(mac, buf, length);
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
xSemaphoreGive(eth_driver->transmit_mutex);
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_eth_transmit_vargs(esp_eth_handle_t hdl, uint32_t argc, ...)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
|
||||
|
||||
if (atomic_load(ð_driver->fsm) != ESP_ETH_FSM_START) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
ESP_LOGD(TAG, "Ethernet is not started");
|
||||
goto err;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
esp_eth_mac_t *mac = eth_driver->mac;
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
if (xSemaphoreTake(eth_driver->transmit_mutex, pdMS_TO_TICKS(ESP_ETH_TX_TIMEOUT_MS)) == pdFALSE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
va_start(args, argc);
|
||||
ret = mac->transmit_vargs(mac, argc, args);
|
||||
#if CONFIG_ETH_TRANSMIT_MUTEX
|
||||
xSemaphoreGive(eth_driver->transmit_mutex);
|
||||
#endif // CONFIG_ETH_TRANSMIT_MUTEX
|
||||
va_end(args);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -416,7 +475,13 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
||||
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|
||||
if (phy->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_PHY_CMDS) {
|
||||
ret = phy->custom_ioctl(phy, cmd, data);
|
||||
} else if (mac->custom_ioctl != NULL && cmd >= ETH_CMD_CUSTOM_MAC_CMDS) {
|
||||
ret = mac->custom_ioctl(mac, cmd, data);
|
||||
} else {
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
err:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdarg.h>
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_attr.h"
|
||||
@@ -234,6 +235,25 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_transmit_multiple_bufs(esp_eth_mac_t *mac, uint32_t argc, va_list args)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent);
|
||||
uint8_t *bufs[argc];
|
||||
uint32_t len[argc];
|
||||
uint32_t exp_len = 0;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
bufs[i] = va_arg(args, uint8_t*);
|
||||
len[i] = va_arg(args, uint32_t);
|
||||
exp_len += len[i];
|
||||
}
|
||||
uint32_t sent_len = emac_hal_transmit_multiple_buf_frame(&emac->hal, bufs, len, argc);
|
||||
ESP_GOTO_ON_FALSE(sent_len == exp_len, ESP_ERR_INVALID_SIZE, err, TAG, "insufficient TX buffer size");
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_esp32_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -619,6 +639,7 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config
|
||||
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;
|
||||
emac->parent.transmit_vargs = emac_esp32_transmit_multiple_bufs;
|
||||
emac->parent.receive = emac_esp32_receive;
|
||||
return &(emac->parent);
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
#include "esp_vfs_l2tap.h"
|
||||
#endif
|
||||
|
||||
const static char *TAG = "esp_eth.netif.netif_glue";
|
||||
|
||||
@@ -27,6 +30,13 @@ struct esp_eth_netif_glue_t {
|
||||
|
||||
static esp_err_t eth_input_to_netif(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv)
|
||||
{
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_vfs_l2tap_eth_filter(eth_handle, buffer, (size_t *)&length);
|
||||
if (length == 0) {
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return esp_netif_receive((esp_netif_t *)priv, buffer, length, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ menu "ESP NETIF Adapter"
|
||||
|
||||
config ESP_NETIF_L2_TAP
|
||||
bool "Enable netif L2 TAP support"
|
||||
select ETH_TRANSMIT_MUTEX
|
||||
help
|
||||
A user program can read/write link layer (L2) frames from/to ESP TAP device.
|
||||
The ESP TAP device can be currently associated only with Ethernet physical interfaces.
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
# ESP-NETIF architecture
|
||||
|
||||
| (A) USER CODE |
|
||||
| |
|
||||
.............| init settings events |
|
||||
. +----------------------------------------+
|
||||
. . | *
|
||||
. . | *
|
||||
--------+ +===========================+ * +-----------------------+
|
||||
| | new/config get/set | * | |
|
||||
| | |...*.......| init |
|
||||
| |---------------------------| * | |
|
||||
init | | |**** | |
|
||||
start |********| event handler |***********| DHCP |
|
||||
stop | | | | |
|
||||
| |---------------------------| | |
|
||||
| | | | NETIF |
|
||||
+-----| | | +-----------------+ |
|
||||
| glue|----<---| esp_netif_transmit |--<--------| netif_output | |
|
||||
| | | | | | | |
|
||||
| |---->---| esp_netif_receive |-->--|-----| netif_input | |
|
||||
| | | | | | + ----------------+ |
|
||||
| |....<...| esp_netif_free_rx_buffer |...<.|..|..| packet buffer |
|
||||
+-----| | | | | | |
|
||||
| | | | | | (D) |
|
||||
(B) | | (C) | | | +-----------------------+
|
||||
--------+ +===========================+ | |
|
||||
communication | | NETWORK STACK
|
||||
DRIVER ESP-NETIF | |
|
||||
| |
|
||||
+-----------------------+ | |
|
||||
| | | |
|
||||
| l2tap_write |-------| |
|
||||
| | |
|
||||
| l2tap_eth_filter |----------|
|
||||
| |
|
||||
| (E) |
|
||||
+-----------------------+
|
||||
|
||||
ESP-NETIF L2 TAP
|
||||
| (A) USER CODE |
|
||||
| |
|
||||
.................| init settings events |
|
||||
. +----------------------------------------+
|
||||
. . | *
|
||||
. . | *
|
||||
--------+ +===========================+ * +-----------------------+
|
||||
| | new/config get/set | * | |
|
||||
| | |...*.....| init |
|
||||
| |---------------------------| * | |
|
||||
init | | |**** | |
|
||||
start |************| event handler |*********| DHCP |
|
||||
stop | | | | |
|
||||
| |---------------------------| | |
|
||||
| | | | NETIF |
|
||||
+-----| | | +-----------------+ |
|
||||
| glue|---<----|---| esp_netif_transmit |--<------| netif_output | |
|
||||
| | | | | | | |
|
||||
| |--->----|---| esp_netif_receive |-->------| netif_input | |
|
||||
| | | | | + ----------------+ |
|
||||
| |...<....|...| esp_netif_free_rx_buffer |...<.....| packet buffer |
|
||||
+-----| | | | | | |
|
||||
| | | | | | (D) |
|
||||
(B) | | | | (C) | +-----------------------+
|
||||
--------+ | | +===========================+
|
||||
communication | | NETWORK STACK
|
||||
DRIVER | | ESP-NETIF
|
||||
| | +------------------+
|
||||
| | +---------------------------+.........| open/close |
|
||||
| | | | | |
|
||||
| -<--| l2tap_write |-----<---| write |
|
||||
| | | | |
|
||||
---->--| esp_vfs_l2tap_eth_filter |----->---| read |
|
||||
| | | |
|
||||
| (E) | +------------------+
|
||||
+---------------------------+
|
||||
USER CODE
|
||||
ESP-NETIF L2 TAP
|
||||
|
||||
## Data/event flow:
|
||||
|
||||
|
||||
@@ -143,88 +143,6 @@ esp_err_t esp_netif_attach(esp_netif_t *esp_netif, esp_netif_iodriver_handle dri
|
||||
*/
|
||||
esp_err_t esp_netif_receive(esp_netif_t *esp_netif, void *buffer, size_t len, void *eb);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ESP_NETIF_L2_TAP_CTRL ESP-NETIF L2 TAP Control API
|
||||
* @brief Functions to control access to ESP-NETIF Data link layer
|
||||
*/
|
||||
|
||||
/** @addtogroup ESP_NETIF_L2_TAP_CTRL
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Add transmit hook callback function reference into ESP-NETIF. This callback function
|
||||
* is then called just prior the ESP-NETIF passes data to network driver.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] hook_fn reference to transmit hook call-back function
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
|
||||
|
||||
/**
|
||||
* @brief Add post transmit hook callback function reference into ESP-NETIF. This callback function
|
||||
* is then called just after the ESP-NETIF passes data to network driver.
|
||||
*
|
||||
* @note Intention of this function is either to release resources allocated by transmit hook function
|
||||
* or for other use cases such as time stamping, etc.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] hook_fn reference to post transmit hook call-back function
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_post_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
|
||||
|
||||
/**
|
||||
* @brief Add receive hook callback function reference into ESP-NETIF. This callback function
|
||||
* is then called when network driver receives data.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] hook_fn reference to receive hook callback function
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_recv_hook_attach(esp_netif_t *esp_netif, void *hook_fn);
|
||||
|
||||
/**
|
||||
* @brief Removes reference to previously attachhed transmit hook callback function
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_transmit_hook_detach(esp_netif_t *esp_netif);
|
||||
|
||||
/**
|
||||
* @brief Removes reference to previously attachhed posttransmit hook callback function
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_post_transmit_hook_detach(esp_netif_t *esp_netif);
|
||||
|
||||
/**
|
||||
* @brief Removes reference to previously attachhed receive hook callback function
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @return
|
||||
* - ESP_OK - success
|
||||
* - ESP_ERR_INVALID_ARG
|
||||
*/
|
||||
esp_err_t esp_netif_recv_hook_detach(esp_netif_t *esp_netif);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#define L2TAP_VFS_DEFAULT_PATH "/dev/net/tap"
|
||||
#define L2TAP_VFS_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.base_path = L2TAP_VFS_DEFAULT_PATH, \
|
||||
.base_path = L2TAP_VFS_DEFAULT_PATH, \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *l2tap_iodriver_handle;
|
||||
|
||||
typedef struct {
|
||||
const char* base_path;
|
||||
} l2tap_vfs_config_t;
|
||||
@@ -28,6 +29,8 @@ typedef enum {
|
||||
L2TAP_G_RCV_FILTER,
|
||||
L2TAP_S_INTF_DEVICE,
|
||||
L2TAP_G_INTF_DEVICE,
|
||||
L2TAP_S_DEVICE_DRV_HNDL,
|
||||
L2TAP_G_DEVICE_DRV_HNDL
|
||||
} l2tap_ioctl_opt_t;
|
||||
|
||||
/**
|
||||
@@ -50,6 +53,17 @@ esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config);
|
||||
*/
|
||||
esp_err_t esp_vfs_l2tap_intf_unregister(const char *base_path);
|
||||
|
||||
/**
|
||||
* @brief Filters received Ethernet L2 frames into L2 TAP infrastructure.
|
||||
*
|
||||
* @param eth_hdl handle of Ethernet driver at which the frame was received
|
||||
* @param buff received L2 frame
|
||||
* @param size input length of the L2 frame which is set to 0 when frame is filtered into L2 TAP
|
||||
* @return esp_err_t
|
||||
* - ESP_OK is always returned
|
||||
*/
|
||||
esp_err_t esp_vfs_l2tap_eth_filter(l2tap_iodriver_handle driver_handle, void *buff, size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -78,13 +78,6 @@ do {
|
||||
action; \
|
||||
} while(0)
|
||||
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
/**
|
||||
* @brief Transmit timeout when multiple accesses to network driver
|
||||
*/
|
||||
#define ESP_NETIF_TX_TIMEOUT 250
|
||||
#endif
|
||||
|
||||
//
|
||||
// Internal types
|
||||
//
|
||||
@@ -502,15 +495,6 @@ esp_netif_t *esp_netif_new(const esp_netif_config_t *esp_netif_config)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_netif->transmit_mutex = xSemaphoreCreateMutex();
|
||||
if (!esp_netif->transmit_mutex) {
|
||||
ESP_LOGE(TAG, "Failed to create L2 TAP transmit mutex");
|
||||
free(esp_netif);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
|
||||
// Create ip info
|
||||
esp_netif_ip_info_t *ip_info = calloc(1, sizeof(esp_netif_ip_info_t));
|
||||
if (!ip_info) {
|
||||
@@ -635,9 +619,6 @@ void esp_netif_destroy(esp_netif_t *esp_netif)
|
||||
esp_netif_destroy_related(esp_netif);
|
||||
free(esp_netif->lwip_netif);
|
||||
free(esp_netif->hostname);
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
vSemaphoreDelete(esp_netif->transmit_mutex);
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_netif_update_default_netif(esp_netif, ESP_NETIF_STOPPED);
|
||||
#if ESP_DHCPS
|
||||
dhcps_delete(esp_netif->dhcps);
|
||||
@@ -711,92 +692,6 @@ esp_err_t esp_netif_get_mac(esp_netif_t *esp_netif, uint8_t mac[])
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_err_t esp_netif_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
if (esp_netif != NULL) {
|
||||
if (esp_netif->transmit_hook == NULL) { // if hook function is not assigned
|
||||
esp_netif->transmit_hook = hook_fn;
|
||||
ret = ESP_OK;
|
||||
} else if (esp_netif->transmit_hook == hook_fn) { // indicate OK when trying to assign the same hook function
|
||||
ret = ESP_OK;
|
||||
}
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_post_transmit_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
if (esp_netif != NULL) {
|
||||
if (esp_netif->post_transmit_hook == NULL) { // if hook function is not assigned
|
||||
esp_netif->post_transmit_hook = hook_fn;
|
||||
ret = ESP_OK;
|
||||
} else if (esp_netif->post_transmit_hook == hook_fn) { // indicate OK when trying to assign the same hook function
|
||||
ret = ESP_OK;
|
||||
}
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_recv_hook_attach(esp_netif_t *esp_netif, void *hook_fn)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
if (esp_netif != NULL) {
|
||||
if (esp_netif->receive_hook == NULL) { // if hook function is not assigned
|
||||
esp_netif->receive_hook = hook_fn;
|
||||
ret = ESP_OK;
|
||||
} else if (esp_netif->receive_hook == hook_fn) { // indicate OK when trying to assign the same hook function
|
||||
ret = ESP_OK;
|
||||
}
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_transmit_hook_detach(esp_netif_t *esp_netif)
|
||||
{
|
||||
if (esp_netif != NULL) {
|
||||
esp_netif->transmit_hook = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_post_transmit_hook_detach(esp_netif_t *esp_netif)
|
||||
{
|
||||
if (esp_netif != NULL) {
|
||||
esp_netif->post_transmit_hook = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_recv_hook_detach(esp_netif_t *esp_netif)
|
||||
{
|
||||
if (esp_netif != NULL) {
|
||||
esp_netif->receive_hook = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
|
||||
#if ESP_DHCPS
|
||||
static void esp_netif_dhcps_cb(void* arg, uint8_t ip[4], uint8_t mac[6])
|
||||
{
|
||||
@@ -1014,28 +909,7 @@ void esp_netif_free_rx_buffer(void *h, void* buffer)
|
||||
|
||||
esp_err_t esp_netif_transmit(esp_netif_t *esp_netif, void* data, size_t len)
|
||||
{
|
||||
esp_err_t ret;
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
if (xSemaphoreTake(esp_netif->transmit_mutex, pdMS_TO_TICKS(ESP_NETIF_TX_TIMEOUT)) == pdFALSE) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (esp_netif->transmit_hook != NULL) {
|
||||
void *p_data = data;
|
||||
esp_netif->transmit_hook(esp_netif, &p_data, &len);
|
||||
ret = (esp_netif->driver_transmit)(esp_netif->driver_handle, p_data, len);
|
||||
if (esp_netif->post_transmit_hook != NULL) {
|
||||
esp_netif->post_transmit_hook(esp_netif, p_data, len);
|
||||
}
|
||||
xSemaphoreGive(esp_netif->transmit_mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
ret = (esp_netif->driver_transmit)(esp_netif->driver_handle, data, len);
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
xSemaphoreGive(esp_netif->transmit_mutex);
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
return ret;
|
||||
return (esp_netif->driver_transmit)(esp_netif->driver_handle, data, len);
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_transmit_wrap(esp_netif_t *esp_netif, void *data, size_t len, void *pbuf)
|
||||
@@ -1045,21 +919,7 @@ esp_err_t esp_netif_transmit_wrap(esp_netif_t *esp_netif, void *data, size_t len
|
||||
|
||||
esp_err_t esp_netif_receive(esp_netif_t *esp_netif, void *buffer, size_t len, void *eb)
|
||||
{
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
if (esp_netif->receive_hook != NULL) {
|
||||
esp_netif->receive_hook(esp_netif, buffer, &len);
|
||||
|
||||
// The receive_hook function may alter original frame (then keeps the len > 0) or it may take full control over the frame and sets len to 0 to
|
||||
// not be populated further
|
||||
if (len > 0) {
|
||||
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
|
||||
}
|
||||
} else {
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
esp_netif->lwip_input_fn(esp_netif->netif_handle, buffer, len, eb);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_lwip_internal.h"
|
||||
|
||||
@@ -110,14 +110,6 @@ 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);
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
SemaphoreHandle_t transmit_mutex;
|
||||
|
||||
// L2 manipulation hooks
|
||||
esp_err_t (*transmit_hook)(void *h, void **buffer, size_t *len);
|
||||
void (*post_transmit_hook)(void *h, void *buffer, size_t len);
|
||||
esp_err_t (*receive_hook)(void *h, void *buffer, size_t *len);
|
||||
#endif
|
||||
|
||||
// dhcp related
|
||||
esp_netif_dhcp_status_t dhcpc_status;
|
||||
|
||||
@@ -293,17 +293,17 @@ static void open_read_task(void *task_param)
|
||||
task_control->eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, task_control->eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(task_control->eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(task_control->eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(task_control->eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(task_control->eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
|
||||
xSemaphoreGive(task_control->sem);
|
||||
|
||||
if (task_control->on_select == true) {
|
||||
@@ -429,17 +429,17 @@ TEST_CASE("esp32 l2tap - non blocking read", "[ethernet][test_env=UT_T2_Ethernet
|
||||
eth_tap_fd = open("/dev/net/tap", O_NONBLOCK);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
|
||||
// ==========================================================
|
||||
// Verify simple non-blocking read operations
|
||||
// ==========================================================
|
||||
@@ -550,17 +550,17 @@ TEST_CASE("esp32 l2tap - blocking read", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
|
||||
// ==========================================================
|
||||
// Verify simple blocking read operations
|
||||
// ==========================================================
|
||||
@@ -610,17 +610,17 @@ TEST_CASE("esp32 l2tap - write", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
|
||||
|
||||
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 = {
|
||||
@@ -674,15 +674,15 @@ static void multi_fds_task (void *task_param)
|
||||
eth_tap_fds[i] = open("/dev/net/tap", O_NONBLOCK);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fds[i]);
|
||||
|
||||
uint16_t eth_type_filter = eth_filter + i;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fds[i], L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fds[i], L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fds[i], L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
uint16_t eth_type_filter = eth_filter + i;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fds[i], L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
}
|
||||
|
||||
int msg_cnt;
|
||||
@@ -763,21 +763,28 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet][test_env=UT_T2_Etherne
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
ESP_LOGI(TAG, "Verify that RCV_FILTER is allowed to be configured only after interface is set...");
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
uint16_t eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
TEST_ASSERT_EQUAL(EACCES, errno);
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
uint16_t eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
|
||||
ESP_LOGI(TAG, "Confirm that we are able to receive message with default Ethertype...");
|
||||
send_task_control_t send_task_ctrl = {
|
||||
.eth_network_hndls_p = ð_network_hndls,
|
||||
@@ -814,10 +821,16 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet][test_env=UT_T2_Etherne
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
|
||||
// Open another raw ethernet fd and try to set the same filter as for the first one
|
||||
// Open another raw ethernet fd at the same interface and try to set the same filter as for the first one
|
||||
int eth_tap_fd_2 = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd_2);
|
||||
ESP_LOGI(TAG, "Verify that the setting the same Ethernet type to other fd was unsuccessful...");
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
// Check the Ethernet interface was assigned
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
ESP_LOGI(TAG, "Verify that the setting the same Ethernet type to other fd at the same interface was unsuccessful...");
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
@@ -836,12 +849,7 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet][test_env=UT_T2_Etherne
|
||||
* @brief Verifies proper functionality of ioctl INTF_DEVICE option
|
||||
*
|
||||
*/
|
||||
static esp_err_t dummy_rx_hook(esp_netif_t *esp_netif, void *buff, size_t *size)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
{
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
int eth_tap_fd;
|
||||
@@ -853,19 +861,24 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE", "[ethernet][test_env=UT_T2_Ethern
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Check getter of direct Ethernet interface handle
|
||||
esp_eth_handle_t l2tap_eth_handle;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_DEVICE_DRV_HNDL, &l2tap_eth_handle));
|
||||
TEST_ASSERT_EQUAL(eth_network_hndls.eth_handle, l2tap_eth_handle);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
uint16_t eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
ESP_LOGI(TAG, "Confirm that we are able to receive message at default netif...");
|
||||
send_task_control_t send_task_ctrl = {
|
||||
@@ -890,16 +903,30 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE", "[ethernet][test_env=UT_T2_Ethern
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // just for sure to give some time to send task close fd
|
||||
|
||||
ESP_LOGI(TAG, "Verify ioctl response when Ethernet interface has already attached recv hook...");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_netif_recv_hook_attach(eth_network_hndls.eth_netif, &dummy_rx_hook));
|
||||
ESP_LOGI(TAG, "Verify that we are able to set Ethernet interface directly via esp_eth_handle..");
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
// Try to set Ethernet interface with already attached recv hook
|
||||
TEST_ASSERT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
TEST_ASSERT_EQUAL(ENOSPC, errno);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL(NULL, if_key_str);
|
||||
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_DEVICE_DRV_HNDL, eth_network_hndls.eth_handle));
|
||||
// Check getter of direct Ethernet interface handle
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_DEVICE_DRV_HNDL, &l2tap_eth_handle));
|
||||
TEST_ASSERT_EQUAL(eth_network_hndls.eth_handle, l2tap_eth_handle);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
|
||||
xTaskCreate(send_task, "raw_eth_send_task", 1024, &send_task_ctrl, tskIDLE_PRIORITY + 2, NULL);
|
||||
n = read(eth_tap_fd, in_buffer, sizeof(in_buffer));
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // just for sure to give some time to send task close fd
|
||||
@@ -949,19 +976,19 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
uint16_t eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
send_task_control_t send_task_ctrl = {
|
||||
.eth_network_hndls_p = ð_network_hndls,
|
||||
@@ -1037,89 +1064,4 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_unregister(NULL));
|
||||
ethernet_deinit(ð_network_hndls);
|
||||
}
|
||||
|
||||
/* ============================================================================= */
|
||||
/**
|
||||
* @brief Verifies option to modify outbound Ethernet frames
|
||||
*
|
||||
*/
|
||||
static char append_msg[] = "!append msg!";
|
||||
static esp_err_t mod_eth_frame(esp_netif_t *esp_netif, void **buff, size_t *size)
|
||||
{
|
||||
uint8_t *mod_buff;
|
||||
|
||||
mod_buff = malloc(*size + sizeof(append_msg));
|
||||
memcpy(mod_buff, *buff, *size);
|
||||
memcpy(&mod_buff[*size], &append_msg, sizeof(append_msg));
|
||||
*buff = mod_buff;
|
||||
*size += sizeof(append_msg);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mod_eth_frame_clear(esp_netif_t *esp_netif, void *buff, size_t size)
|
||||
{
|
||||
free(buff);
|
||||
}
|
||||
|
||||
TEST_CASE("esp32 l2tap - netif tx hook", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
{
|
||||
test_vfs_eth_network_t eth_network_hndls;
|
||||
int eth_tap_fd;
|
||||
char in_buffer[1500] = { 0 };
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_register(NULL));
|
||||
ethernet_init(ð_network_hndls);
|
||||
|
||||
eth_tap_fd = open("/dev/net/tap", 0);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, eth_tap_fd);
|
||||
|
||||
// Set the Ethertype filter (frames with this type will be available through the eth_tap_fd)
|
||||
uint16_t eth_type_filter = ETH_FILTER_LE;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_RCV_FILTER, ð_type_filter));
|
||||
uint16_t eth_type_filter_get = 0;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, ð_type_filter_get));
|
||||
TEST_ASSERT_EQUAL(eth_type_filter, eth_type_filter_get);
|
||||
// Set Ethernet interface on which to get raw frames
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_S_INTF_DEVICE, "ETH_DEF"));
|
||||
|
||||
// Check the Ethernet interface was assigned
|
||||
char *if_key_str;
|
||||
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_INTF_DEVICE, &if_key_str));
|
||||
TEST_ASSERT_EQUAL_STRING("ETH_DEF", if_key_str);
|
||||
|
||||
send_task_control_t send_task_ctrl = {
|
||||
.eth_network_hndls_p = ð_network_hndls,
|
||||
.eth_type = -1,
|
||||
.send_delay_ms = 0,
|
||||
};
|
||||
// Send normal test message without any registered transmit hook at first
|
||||
xTaskCreate(send_task, "raw_eth_send_task", 1024, &send_task_ctrl, tskIDLE_PRIORITY + 2, NULL);
|
||||
int n = read(eth_tap_fd, in_buffer, sizeof(in_buffer));
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
TEST_ASSERT_EQUAL(sizeof(s_test_msg), n);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n);
|
||||
|
||||
// Attach transmit hook function to modify outbound Ethernet frames
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_netif_transmit_hook_attach(eth_network_hndls.eth_netif, &mod_eth_frame));
|
||||
|
||||
// Attach post transmit hook function to clear buffer created by mod_eth_frame
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_netif_post_transmit_hook_attach(eth_network_hndls.eth_netif, &mod_eth_frame_clear));
|
||||
|
||||
// Verify the outbound traffic was modified as expected
|
||||
xTaskCreate(send_task, "raw_eth_send_task", 1024, &send_task_ctrl, tskIDLE_PRIORITY + 2, NULL);
|
||||
n = read(eth_tap_fd, in_buffer, sizeof(in_buffer));
|
||||
TEST_ASSERT_GREATER_THAN(0, n);
|
||||
TEST_ASSERT_EQUAL(sizeof(s_test_msg) + sizeof(append_msg), n);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&s_test_msg, in_buffer, n - sizeof(append_msg));
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(&append_msg, &in_buffer[sizeof(s_test_msg)], n - sizeof(s_test_msg));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_netif_transmit_hook_detach(eth_network_hndls.eth_netif));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_netif_post_transmit_hook_detach(eth_network_hndls.eth_netif));
|
||||
TEST_ASSERT_EQUAL(0, close(eth_tap_fd));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50)); // just for sure to give some time to send task close fd
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_unregister(NULL));
|
||||
ethernet_deinit(ð_network_hndls);
|
||||
}
|
||||
#endif // CONFIG_ESP_NETIF_L2_TAP
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -18,13 +18,12 @@
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
|
||||
#if CONFIG_ESP_NETIF_L2_TAP
|
||||
|
||||
#define INVALID_FD (-1)
|
||||
@@ -41,10 +40,13 @@ typedef enum {
|
||||
typedef struct {
|
||||
_Atomic l2tap_socket_state_t state;
|
||||
bool non_blocking;
|
||||
esp_netif_t *netif;
|
||||
l2tap_iodriver_handle driver_handle;
|
||||
uint16_t ethtype_filter;
|
||||
QueueHandle_t rx_queue;
|
||||
SemaphoreHandle_t close_done_sem;
|
||||
|
||||
esp_err_t (*driver_transmit)(l2tap_iodriver_handle io_handle, void *buffer, size_t len);
|
||||
void (*driver_free_rx_buffer)(l2tap_iodriver_handle io_handle, void* buffer);
|
||||
} l2tap_context_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -122,7 +124,7 @@ static ssize_t pop_rx_queue(l2tap_context_t *l2tap_socket, void *buff, size_t le
|
||||
len = frame_info.len;
|
||||
}
|
||||
memcpy(buff, frame_info.buff, len);
|
||||
free(frame_info.buff);
|
||||
l2tap_socket->driver_free_rx_buffer(l2tap_socket->driver_handle, frame_info.buff);
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
@@ -163,8 +165,13 @@ static inline void l2tap_unlock(void)
|
||||
portEXIT_CRITICAL(&s_critical_section_lock);
|
||||
}
|
||||
|
||||
/* ================== ESP NETIF intf ====================== */
|
||||
esp_err_t l2tap_eth_filter(esp_netif_t *esp_netif, void *buff, size_t *size)
|
||||
static inline void default_free_rx_buffer(l2tap_iodriver_handle io_handle, void* buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/* ================== ESP NETIF L2 TAP intf ====================== */
|
||||
esp_err_t esp_vfs_l2tap_eth_filter(l2tap_iodriver_handle driver_handle, void *buff, size_t *size)
|
||||
{
|
||||
struct eth_hdr *eth_header = buff;
|
||||
uint16_t eth_type = ntohs(eth_header->type);
|
||||
@@ -172,14 +179,14 @@ esp_err_t l2tap_eth_filter(esp_netif_t *esp_netif, void *buff, size_t *size)
|
||||
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
|
||||
if (atomic_load(&s_l2tap_sockets[i].state) == L2TAP_SOCK_STATE_OPENED) {
|
||||
l2tap_lock(); // read of socket config needs to be atomic since it can be manipulated from other task
|
||||
if (s_l2tap_sockets[i].netif == esp_netif && (s_l2tap_sockets[i].ethtype_filter == eth_type ||
|
||||
if (s_l2tap_sockets[i].driver_handle == driver_handle && (s_l2tap_sockets[i].ethtype_filter == eth_type ||
|
||||
// IEEE 802.2 Frame is identified based on its length which is less than IEEE802.3 max length (Ethernet II Types IDs start over this value)
|
||||
// Note that IEEE 802.2 LLC resolution is expected to be performed by upper stream app
|
||||
(s_l2tap_sockets[i].ethtype_filter <= ETH_IEEE802_3_MAX_LEN && eth_type <= ETH_IEEE802_3_MAX_LEN))) {
|
||||
l2tap_unlock();
|
||||
if (push_rx_queue(&s_l2tap_sockets[i], buff, *size) != ESP_OK) {
|
||||
// just tail drop when queue is full
|
||||
free(buff);
|
||||
s_l2tap_sockets[i].driver_free_rx_buffer(s_l2tap_sockets[i].driver_handle, buff);
|
||||
ESP_LOGD(TAG, "fd %d rx queue is full", i);
|
||||
}
|
||||
l2tap_lock();
|
||||
@@ -188,13 +195,11 @@ esp_err_t l2tap_eth_filter(esp_netif_t *esp_netif, void *buff, size_t *size)
|
||||
}
|
||||
l2tap_unlock();
|
||||
*size = 0; // the frame is not passed to IP stack when size set to 0
|
||||
goto end;
|
||||
} else {
|
||||
l2tap_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -213,8 +218,10 @@ static int l2tap_open(const char *path, int flags, int mode)
|
||||
goto err;
|
||||
}
|
||||
s_l2tap_sockets[fd].ethtype_filter = 0x0;
|
||||
s_l2tap_sockets[fd].netif = NULL;
|
||||
s_l2tap_sockets[fd].driver_handle = NULL;
|
||||
s_l2tap_sockets[fd].non_blocking = ((flags & O_NONBLOCK) == O_NONBLOCK);
|
||||
s_l2tap_sockets[fd].driver_transmit = esp_eth_transmit;
|
||||
s_l2tap_sockets[fd].driver_free_rx_buffer = default_free_rx_buffer;
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
@@ -238,7 +245,7 @@ static ssize_t l2tap_write(int fd, const void *data, size_t size)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (esp_netif_transmit(s_l2tap_sockets[fd].netif, (void *)data, size) == ESP_OK) {
|
||||
if (s_l2tap_sockets[fd].driver_transmit(s_l2tap_sockets[fd].driver_handle, (void *)data, size) == ESP_OK) {
|
||||
ret = size;
|
||||
} else {
|
||||
// I/O error
|
||||
@@ -273,46 +280,6 @@ static ssize_t l2tap_read(int fd, void *data, size_t size)
|
||||
return actual_size;
|
||||
}
|
||||
|
||||
// This function checks if any other socket is associated with inputting ESP netif. If there is no other socket associated with that netif, Raw Ethernet
|
||||
// filter function is detached so the netif hook could be used by other components.
|
||||
static esp_err_t l2tap_eth_filter_fn_detach(l2tap_context_t *l2tap_socket)
|
||||
{
|
||||
esp_netif_t *netif = l2tap_socket->netif;
|
||||
if (netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
l2tap_socket->netif = NULL;
|
||||
|
||||
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
|
||||
if (s_l2tap_sockets[i].netif == netif) {
|
||||
// other socket is associated to the same ESP netif, keep the filter function attached
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// no socket is associated to the ESP netif, we are free to detach the filter function
|
||||
return esp_netif_recv_hook_detach(netif);
|
||||
}
|
||||
|
||||
static esp_err_t l2tap_eth_filter_fn_attach(l2tap_context_t *l2tap_socket, esp_netif_t *new_netif)
|
||||
{
|
||||
if (new_netif == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// check if filter function can be detached from the old netif
|
||||
l2tap_eth_filter_fn_detach(l2tap_socket);
|
||||
|
||||
l2tap_socket->netif = new_netif;
|
||||
// Try to attach filter function to newly selected netif
|
||||
if (esp_netif_recv_hook_attach(l2tap_socket->netif, &l2tap_eth_filter) != ESP_OK) {
|
||||
l2tap_socket->netif = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void l2tap_clean_task(void *task_param)
|
||||
{
|
||||
l2tap_context_t *l2tap_socket = (l2tap_context_t *)task_param;
|
||||
@@ -327,12 +294,6 @@ void l2tap_clean_task(void *task_param)
|
||||
flush_rx_queue(l2tap_socket);
|
||||
delete_rx_queue(l2tap_socket);
|
||||
|
||||
// check if filter function can be detached & detach
|
||||
// lock, we don't want to other tasks modify other sockets context info
|
||||
l2tap_lock();
|
||||
l2tap_eth_filter_fn_detach(l2tap_socket);
|
||||
l2tap_unlock();
|
||||
|
||||
// unblock task which originally called close
|
||||
xSemaphoreGive(l2tap_socket->close_done_sem);
|
||||
|
||||
@@ -374,15 +335,24 @@ static int l2tap_close(int fd)
|
||||
|
||||
static int l2tap_ioctl(int fd, int cmd, va_list args)
|
||||
{
|
||||
esp_netif_t *esp_netif;
|
||||
switch (cmd) {
|
||||
case L2TAP_S_RCV_FILTER: ;
|
||||
uint16_t *new_ethtype_filter = va_arg(args, uint16_t *);
|
||||
l2tap_lock();
|
||||
// socket needs to be assigned to interface at first
|
||||
if (s_l2tap_sockets[fd].driver_handle == NULL) {
|
||||
// Permission denied (filter change is denied at this state)
|
||||
errno = EACCES;
|
||||
l2tap_unlock();
|
||||
goto err;
|
||||
}
|
||||
// do nothing when same filter is to be set
|
||||
if (s_l2tap_sockets[fd].ethtype_filter != *new_ethtype_filter) {
|
||||
// check if the ethtype filter is not already used by other socket
|
||||
// check if the ethtype filter is not already used by other socket of the same interface
|
||||
for (int i = 0; i < L2TAP_MAX_FDS; i++) {
|
||||
if (atomic_load(&s_l2tap_sockets[i].state) == L2TAP_SOCK_STATE_OPENED &&
|
||||
s_l2tap_sockets[i].driver_handle == s_l2tap_sockets[fd].driver_handle &&
|
||||
s_l2tap_sockets[i].ethtype_filter == *new_ethtype_filter) {
|
||||
// invalid argument
|
||||
errno = EINVAL;
|
||||
@@ -400,29 +370,41 @@ static int l2tap_ioctl(int fd, int cmd, va_list args)
|
||||
break;
|
||||
case L2TAP_S_INTF_DEVICE: ;
|
||||
const char *str = va_arg(args, const char *);
|
||||
esp_netif_t *new_netif = esp_netif_get_handle_from_ifkey(str);
|
||||
if (new_netif == NULL) {
|
||||
esp_netif = esp_netif_get_handle_from_ifkey(str);
|
||||
if (esp_netif == NULL) {
|
||||
// No such device
|
||||
errno = ENODEV;
|
||||
goto err;
|
||||
}
|
||||
l2tap_lock();
|
||||
if (l2tap_eth_filter_fn_attach(&s_l2tap_sockets[fd], new_netif) != ESP_OK) {
|
||||
// No space on device - netif hook is already taken
|
||||
errno = ENOSPC;
|
||||
l2tap_unlock();
|
||||
goto err;
|
||||
}
|
||||
s_l2tap_sockets[fd].driver_handle = esp_netif_get_io_driver(esp_netif);
|
||||
l2tap_unlock();
|
||||
break;
|
||||
case L2TAP_G_INTF_DEVICE: ;
|
||||
const char **str_p = va_arg(args, const char **);
|
||||
if (s_l2tap_sockets[fd].netif != NULL) {
|
||||
*str_p = esp_netif_get_ifkey(s_l2tap_sockets[fd].netif);
|
||||
} else {
|
||||
*str_p = NULL;
|
||||
*str_p = NULL;
|
||||
esp_netif = NULL;
|
||||
while ((esp_netif = esp_netif_next(esp_netif)) != NULL) {
|
||||
if (s_l2tap_sockets[fd].driver_handle == esp_netif_get_io_driver(esp_netif)) {
|
||||
*str_p = esp_netif_get_ifkey(esp_netif);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case L2TAP_S_DEVICE_DRV_HNDL: ;
|
||||
l2tap_iodriver_handle set_driver_hdl = va_arg(args, l2tap_iodriver_handle);
|
||||
if (set_driver_hdl == NULL) {
|
||||
// No such device (not valid driver handle)
|
||||
errno = ENODEV;
|
||||
goto err;
|
||||
}
|
||||
l2tap_lock();
|
||||
s_l2tap_sockets[fd].driver_handle = set_driver_hdl;
|
||||
l2tap_unlock();
|
||||
break;
|
||||
case L2TAP_G_DEVICE_DRV_HNDL: ;
|
||||
l2tap_iodriver_handle *get_driver_hdl = va_arg(args, l2tap_iodriver_handle*);
|
||||
*get_driver_hdl = s_l2tap_sockets[fd].driver_handle;
|
||||
break;
|
||||
default:
|
||||
// unsupported operation
|
||||
errno = ENOSYS;
|
||||
|
||||
@@ -449,6 +449,94 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t emac_hal_transmit_multiple_buf_frame(emac_hal_context_t *hal, uint8_t **buffs, uint32_t *lengths, uint32_t buffs_cnt)
|
||||
{
|
||||
/* Get the number of Tx buffers to use for the frame */
|
||||
uint32_t dma_bufcount = 0;
|
||||
uint32_t sentout = 0;
|
||||
uint8_t *ptr = buffs[0];
|
||||
uint32_t lastlen = lengths[0];
|
||||
uint32_t avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
|
||||
|
||||
eth_dma_tx_descriptor_t *desc_iter = hal->tx_desc;
|
||||
/* A frame is transmitted in multiple descriptor */
|
||||
while (dma_bufcount < CONFIG_ETH_DMA_TX_BUFFER_NUM) {
|
||||
/* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */
|
||||
if (desc_iter->TDES0.Own != EMAC_LL_DMADESC_OWNER_CPU) {
|
||||
goto err;
|
||||
}
|
||||
/* Clear FIRST and LAST segment bits */
|
||||
desc_iter->TDES0.FirstSegment = 0;
|
||||
desc_iter->TDES0.LastSegment = 0;
|
||||
desc_iter->TDES0.InterruptOnComplete = 0;
|
||||
desc_iter->TDES1.TransmitBuffer1Size = 0;
|
||||
if (dma_bufcount == 0) {
|
||||
/* Setting the first segment bit */
|
||||
desc_iter->TDES0.FirstSegment = 1;
|
||||
}
|
||||
|
||||
while (buffs_cnt > 0) {
|
||||
/* Check if input buff data fits to currently available space in the descriptor */
|
||||
if (lastlen < avail_len) {
|
||||
/* copy data from uplayer stack buffer */
|
||||
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, lastlen);
|
||||
sentout += lastlen;
|
||||
avail_len -= lastlen;
|
||||
desc_iter->TDES1.TransmitBuffer1Size += lastlen;
|
||||
|
||||
/* Update processed input buffers info */
|
||||
buffs_cnt--;
|
||||
ptr = *(++buffs);
|
||||
lastlen = *(++lengths);
|
||||
/* There is only limited available space in the current descriptor, use it all */
|
||||
} else {
|
||||
/* copy data from uplayer stack buffer */
|
||||
memcpy((void *)(desc_iter->Buffer1Addr + (CONFIG_ETH_DMA_BUFFER_SIZE - avail_len)), ptr, avail_len);
|
||||
sentout += avail_len;
|
||||
lastlen -= avail_len;
|
||||
/* If lastlen is not zero, input buff will be fragmented over multiple descriptors */
|
||||
if (lastlen > 0) {
|
||||
ptr += avail_len;
|
||||
/* Input buff fully fits the descriptor, move to the next input buff */
|
||||
} else {
|
||||
/* Update processed input buffers info */
|
||||
buffs_cnt--;
|
||||
ptr = *(++buffs);
|
||||
lastlen = *(++lengths);
|
||||
}
|
||||
avail_len = CONFIG_ETH_DMA_BUFFER_SIZE;
|
||||
desc_iter->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE;
|
||||
/* The descriptor is full here so exit and use the next descriptor */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Increase counter of utilized DMA buffers */
|
||||
dma_bufcount++;
|
||||
|
||||
/* If all input buffers processed, mark as LAST segment and finish the coping */
|
||||
if (buffs_cnt == 0) {
|
||||
/* Setting the last segment bit */
|
||||
desc_iter->TDES0.LastSegment = 1;
|
||||
/* Enable transmit interrupt */
|
||||
desc_iter->TDES0.InterruptOnComplete = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Point to next descriptor */
|
||||
desc_iter = (eth_dma_tx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
|
||||
}
|
||||
|
||||
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
|
||||
for (size_t i = 0; i < dma_bufcount; i++) {
|
||||
hal->tx_desc->TDES0.Own = EMAC_LL_DMADESC_OWNER_DMA;
|
||||
hal->tx_desc = (eth_dma_tx_descriptor_t *)(hal->tx_desc->Buffer2NextDescAddr);
|
||||
}
|
||||
emac_ll_transmit_poll_demand(hal->dma_regs, 0);
|
||||
return sentout;
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc)
|
||||
{
|
||||
eth_dma_rx_descriptor_t *desc_iter = NULL;
|
||||
|
||||
@@ -241,6 +241,8 @@ uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal);
|
||||
|
||||
uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length);
|
||||
|
||||
uint32_t emac_hal_transmit_multiple_buf_frame(emac_hal_context_t *hal, uint8_t **buffs, uint32_t *lengths, uint32_t inbuffs_cnt);
|
||||
|
||||
uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc);
|
||||
|
||||
void emac_hal_enable_flow_ctrl(emac_hal_context_t *hal, bool enable);
|
||||
|
||||
Reference in New Issue
Block a user