diff --git a/components/esp_eth/Kconfig b/components/esp_eth/Kconfig index ca118a8460..8f85800a04 100644 --- a/components/esp_eth/Kconfig +++ b/components/esp_eth/Kconfig @@ -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 diff --git a/components/esp_eth/include/esp_eth.h b/components/esp_eth/include/esp_eth.h index ee43281e81..ab19f210cf 100644 --- a/components/esp_eth/include/esp_eth.h +++ b/components/esp_eth/include/esp_eth.h @@ -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); diff --git a/components/esp_eth/include/esp_eth_mac.h b/components/esp_eth/include/esp_eth_mac.h index c054cafcf9..c3a514fe00 100644 --- a/components/esp_eth/include/esp_eth_mac.h +++ b/components/esp_eth/include/esp_eth_mac.h @@ -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 * diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index 6e55d33b19..ebba324f6b 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -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 * diff --git a/components/esp_eth/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index e0e791fc73..54734bd45a 100644 --- a/components/esp_eth/src/esp_eth.c +++ b/components/esp_eth/src/esp_eth.c @@ -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: diff --git a/components/esp_eth/src/esp_eth_mac_esp.c b/components/esp_eth/src/esp_eth_mac_esp.c index e32fc9fe72..5140e0420b 100644 --- a/components/esp_eth/src/esp_eth_mac_esp.c +++ b/components/esp_eth/src/esp_eth_mac_esp.c @@ -6,6 +6,7 @@ #include #include #include +#include #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); diff --git a/components/esp_eth/src/esp_eth_netif_glue.c b/components/esp_eth/src/esp_eth_netif_glue.c index b87bb28563..58026bea8c 100644 --- a/components/esp_eth/src/esp_eth_netif_glue.c +++ b/components/esp_eth/src/esp_eth_netif_glue.c @@ -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); } diff --git a/components/esp_netif/Kconfig b/components/esp_netif/Kconfig index ac8e0ea64d..090b590145 100644 --- a/components/esp_netif/Kconfig +++ b/components/esp_netif/Kconfig @@ -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. diff --git a/components/esp_netif/README.md b/components/esp_netif/README.md index 2e1126c1ce..c2000c8cb4 100644 --- a/components/esp_netif/README.md +++ b/components/esp_netif/README.md @@ -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: diff --git a/components/esp_netif/include/esp_netif.h b/components/esp_netif/include/esp_netif.h index 241be53da1..5cefaea813 100644 --- a/components/esp_netif/include/esp_netif.h +++ b/components/esp_netif/include/esp_netif.h @@ -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); - /** * @} */ diff --git a/components/esp_netif/include/esp_vfs_l2tap.h b/components/esp_netif/include/esp_vfs_l2tap.h index cb35da0899..bb26ec23d0 100644 --- a/components/esp_netif/include/esp_vfs_l2tap.h +++ b/components/esp_netif/include/esp_vfs_l2tap.h @@ -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 diff --git a/components/esp_netif/lwip/esp_netif_lwip.c b/components/esp_netif/lwip/esp_netif_lwip.c index 1497460363..390b9708f8 100644 --- a/components/esp_netif/lwip/esp_netif_lwip.c +++ b/components/esp_netif/lwip/esp_netif_lwip.c @@ -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; } diff --git a/components/esp_netif/lwip/esp_netif_lwip_defaults.c b/components/esp_netif/lwip/esp_netif_lwip_defaults.c index 28e623e688..dc71d61e18 100644 --- a/components/esp_netif/lwip/esp_netif_lwip_defaults.c +++ b/components/esp_netif/lwip/esp_netif_lwip_defaults.c @@ -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" diff --git a/components/esp_netif/lwip/esp_netif_lwip_internal.h b/components/esp_netif/lwip/esp_netif_lwip_internal.h index 3ef6702ab1..66607fe495 100644 --- a/components/esp_netif/lwip/esp_netif_lwip_internal.h +++ b/components/esp_netif/lwip/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; diff --git a/components/esp_netif/test/test_vfs_l2tap.c b/components/esp_netif/test/test_vfs_l2tap.c index 61b75e398c..5fbd7f844d 100644 --- a/components/esp_netif/test/test_vfs_l2tap.c +++ b/components/esp_netif/test/test_vfs_l2tap.c @@ -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 diff --git a/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c b/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c index 117bf44e8b..e6e93800d0 100644 --- a/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c +++ b/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c @@ -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; diff --git a/components/hal/emac_hal.c b/components/hal/emac_hal.c index 0c1a310b27..55ff69f5ab 100644 --- a/components/hal/emac_hal.c +++ b/components/hal/emac_hal.c @@ -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; diff --git a/components/hal/include/hal/emac_hal.h b/components/hal/include/hal/emac_hal.h index 91bcf59f9c..4971b1aaf5 100644 --- a/components/hal/include/hal/emac_hal.h +++ b/components/hal/include/hal/emac_hal.h @@ -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); diff --git a/docs/en/api-reference/network/esp_netif.rst b/docs/en/api-reference/network/esp_netif.rst index e4da1000b5..c635fbe2d1 100644 --- a/docs/en/api-reference/network/esp_netif.rst +++ b/docs/en/api-reference/network/esp_netif.rst @@ -18,44 +18,44 @@ ESP-NETIF architecture .. code-block:: text - | (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 and event flow in the diagram @@ -145,8 +145,8 @@ E) ESP-NETIF L2 TAP Interface The ESP-NETIF L2 TAP interface is ESP-IDF mechanism utilized to access Data Link Layer (L2 per OSI/ISO) for frame reception and transmission from user application. Its typical usage in embedded world might be implementation of non-IP related protocols such as PTP, Wake on LAN and others. Note that only Ethernet (IEEE 802.3) is currently supported. From user perspective, the ESP-NETIF L2 TAP interface is accessed using file descriptors of VFS which provides a file-like interfacing (using functions like ``open()``, ``read()``, ``write()``, etc). Refer to :doc:`/api-reference/storage/vfs` to learn more. - -There is only one ESP-NETIF L2 TAP interface device (path name) available. However multiple file descriptors with different configuration can be opened at a time since the ESP-NETIF L2 TAP interface can be understood as generic entry point to the NETIF internal structure. Important is then specific configuration of particular file descriptor. It can be configured to give an access to specific Network Interface identified by ``if_key`` (e.g. `ETH_DEF`) and to filter only specific frames based on their type (e.g. Ethernet type in case of IEEE 802.3). Filtering only specific frames is crucial since the ESP-NETIF L2 TAP needs to work along with IP stack and so the IP related traffic (IP, ARP, etc.) should not be passed directly to the user application. Even though such option is still configurable, it is not recommended in standard use cases. Filtering is also advantageous from a perspective the user’s application gets access only to frame types it is interested in and the remaining traffic is either passed to other L2 TAP file descriptors or to IP stack. + +There is only one ESP-NETIF L2 TAP interface device (path name) available. However multiple file descriptors with different configuration can be opened at a time since the ESP-NETIF L2 TAP interface can be understood as generic entry point to Layer 2 infrastructure. Important is then specific configuration of particular file descriptor. It can be configured to give an access to specific Network Interface identified by ``if_key`` (e.g. `ETH_DEF`) and to filter only specific frames based on their type (e.g. Ethernet type in case of IEEE 802.3). Filtering only specific frames is crucial since the ESP-NETIF L2 TAP needs to exist along with IP stack and so the IP related traffic (IP, ARP, etc.) should not be passed directly to the user application. Even though such option is still configurable, it is not recommended in standard use cases. Filtering is also advantageous from a perspective the user’s application gets access only to frame types it is interested in and the remaining traffic is either passed to other L2 TAP file descriptors or to IP stack. ESP-NETIF L2 TAP Interface Usage Manual --------------------------------------- @@ -157,7 +157,7 @@ To be able to use the ESP-NETIF L2 TAP interface, it needs to be enabled in Kcon open() ^^^^^^ -Once the ESP-NETIF L2 TAP is registered, it can be opened at path name “/dev/net/tap”. The same path path name can be opened multiple times up to :ref:`CONFIG_ESP_NETIF_L2_TAP_MAX_FDS` and multiple file descriptors with with different configuration may access the Data Link Layer in the NETIF. +Once the ESP-NETIF L2 TAP is registered, it can be opened at path name “/dev/net/tap”. The same path name can be opened multiple times up to :ref:`CONFIG_ESP_NETIF_L2_TAP_MAX_FDS` and multiple file descriptors with with different configuration may access the Data Link Layer frames. The ESP-NETIF L2 TAP can be opened with ``O_NONBLOCK`` file status flag to the ``read()`` does not block. Note that the ``write()`` may block in current implementation when accessing a Network interface since it is a shared resource among multiple ESP-NETIF L2 TAP file descriptors and IP stack, and there is currently no queuing mechanism deployed. The file status flag can be retrieved and modified using ``fcntl()``. @@ -167,19 +167,26 @@ ioctl() ^^^^^^^ The newly opened ESP-NETIF L2 TAP file descriptor needs to be configured prior its usage since it is not bounded to any specific Network Interface and no frame type filter is configured. The following configuration options are available to do so: - * ``L2TAP_S_INTF_DEVICE`` - bounds the file descriptor to specific Network Interface which is identified by its ``if_key``. Network Interface ``if_key`` is passed to ``ioctl()`` as the third parameter. Note that default Network Interfaces ``if_key``'s used in ESP-IDF can be found in :component_file:`esp_netif/include/esp_netif_defaults.h`. + * ``L2TAP_S_INTF_DEVICE`` - bounds the file descriptor to specific Network Interface which is identified by its ``if_key``. ESP-NETIF Network Interface ``if_key`` is passed to ``ioctl()`` as the third parameter. Note that default Network Interfaces ``if_key``'s used in ESP-IDF can be found in :component_file:`esp_netif/include/esp_netif_defaults.h`. + * ``L2TAP_S_DEVICE_DRV_HNDL`` - is other way how to bound the file descriptor to specific Network Interface. In this case the Network interface is identified directly by IO Driver handle (e.g. :cpp:type:`esp_eth_handle_t` in case of Ethernet). The IO Driver handle is passed to ``ioctl()`` as the third parameter. * ``L2TAP_S_RCV_FILTER`` - sets the filter to frames with this type to be passed to the file descriptor. In case of Ethernet frames, the frames are to be filtered based on Length/Ethernet type field. In case the filter value is set less than or equal to 0x05DC, the Ethernet type field is considered to represent IEEE802.3 Length Field and all frames with values in interval <0, 0x05DC> at that field are to be passed to the file descriptor. The IEEE802.2 logical link control (LLC) resolution is then expected to be performed by user’s application. In case the filter value is set greater than 0x05DC, the Ethernet type field is considered to represent protocol identification and only frames which are equal to the set value are to be passed to the file descriptor. -All set configuration options have getter counterpart option to read the current settings. +All above set configuration options have getter counterpart option to read the current settings. + +.. warning:: + The file descriptor needs to be firstly bounded to specific Network Interface by ``L2TAP_S_INTF_DEVICE`` or ``L2TAP_S_DEVICE_DRV_HNDL`` to be ``L2TAP_S_RCV_FILTER`` option available. .. note:: VLAN tagged frames are currently not recognized. If user needs to process VLAN tagged frames, they need set filter to be equal to VLAN tag (i.e. 0x8100 or 0x88A8) and process the VLAN tagged frames in user application. +.. note:: + ``L2TAP_S_DEVICE_DRV_HNDL`` is particularly useful when user's application does not require usage of IP stack and so ESP-NETIF is not required to be initialized too. As a result, Network Interface cannot be identified by its ``if_key`` and hence it needs to be identified directly by its IO Driver handle. + | On success, ``ioctl()`` returns 0. On error, -1 is returned, and ``errno`` is set to indicate the error. | **EBADF** - not a valid file descriptor. -| **EINVAL** - invalid configuration argument. Ethernet type filter is already used by other file descriptor. +| **EACCES** - option change is denied in this state (e.g. file descriptor has not be bounded to Network interface yet). +| **EINVAL** - invalid configuration argument. Ethernet type filter is already used by other file descriptor on that same Network interface. | **ENODEV** - no such Network Interface which is tried to be assigned to the file descriptor exists. -| **ENOSPC** - NETIF L2 receive hook is already taken by other function when trying to assign Network Interface to the file descriptor. | **ENOSYS** - unsupported operation, passed configuration option does not exists. read() diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index b945905369..0923ab4f62 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -479,7 +479,6 @@ components/esp_local_ctrl/src/esp_local_ctrl_transport_httpd.c components/esp_netif/include/esp_netif_ppp.h components/esp_netif/include/esp_netif_slip.h components/esp_netif/loopback/esp_netif_loopback.c -components/esp_netif/lwip/esp_netif_lwip_defaults.c components/esp_netif/lwip/esp_netif_lwip_ppp.h components/esp_netif/lwip/esp_netif_lwip_slip.c components/esp_netif/lwip/esp_netif_lwip_slip.h