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:
morris
2022-04-14 10:51:27 +08:00
20 changed files with 537 additions and 562 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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
*

View File

@@ -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
*

View File

@@ -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, &eth_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(&eth_driver->ref_count, 1);
atomic_init(&eth_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(&eth_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:

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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:

View File

@@ -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);
/**
* @}
*/

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_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, &eth_type_filter));
uint16_t eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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, &eth_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, &eth_type_filter));
uint16_t eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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 = &eth_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, &eth_type_filter));
TEST_ASSERT_EQUAL(EINVAL, errno);
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd_2, L2TAP_G_RCV_FILTER, &eth_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, &eth_type_filter));
uint16_t eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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, &eth_type_filter));
eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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, &eth_type_filter));
uint16_t eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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 = &eth_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(&eth_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(&eth_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, &eth_type_filter));
uint16_t eth_type_filter_get = 0;
TEST_ASSERT_NOT_EQUAL(-1, ioctl(eth_tap_fd, L2TAP_G_RCV_FILTER, &eth_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 = &eth_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(&eth_network_hndls);
}
#endif // CONFIG_ESP_NETIF_L2_TAP

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);