Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[eppp]: Support for transport via Ethernet (phy or emac) #622

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/eppp__build.yml
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
idf_ver: ["latest", "release-v5.3"]
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: host, path: "examples/emac2emac" }, { app: test_app, path: "test/test_app" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
17 changes: 15 additions & 2 deletions components/eppp_link/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
idf_component_register(SRCS eppp_link.c eppp_sdio_slave.c eppp_sdio_host.c
if(CONFIG_EPPP_LINK_DEVICE_ETH)
set(transport_src eppp_eth.c)
endif()

if(CONFIG_EPPP_LINK_DEVICE_SDIO)
set(transport_src eppp_sdio_slave.c eppp_sdio_host.c)
endif()

idf_component_register(SRCS eppp_link.c ${transport_src}
INCLUDE_DIRS "include"
PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver)
PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver esp_eth)

if(CONFIG_EPPP_LINK_DEVICE_ETH)
idf_component_get_property(ethernet_init espressif__ethernet_init COMPONENT_LIB)
target_link_libraries(${COMPONENT_LIB} PRIVATE ${ethernet_init})
endif()
20 changes: 20 additions & 0 deletions components/eppp_link/Kconfig
Original file line number Diff line number Diff line change
@@ -28,6 +28,16 @@ menu "eppp_link"
help
Use SDIO.

config EPPP_LINK_DEVICE_ETH
bool "Ethernet"
depends on SOC_EMAC_SUPPORTED
help
Use Ethernet.
This transport could employ a full fledged Ethernet connection
between two EPPP nodes via standard Ethernet cable.
It could be also effectively connected directly on PCB, EMAC to EMAC,
without any Ethernet PHY chips (using eth_dummy_phy driver).

endchoice

config EPPP_LINK_CONN_MAX_RETRY
@@ -67,4 +77,14 @@ menu "eppp_link"

endchoice

config EPPP_LINK_ETHERNET_OUR_ADDRESS
string "MAC address our local node"
default "06:00:00:00:00:01"
depends on EPPP_LINK_DEVICE_ETH

config EPPP_LINK_ETHERNET_THEIR_ADDRESS
string "MAC address the remote node"
default "06:00:00:00:00:02"
depends on EPPP_LINK_DEVICE_ETH

endmenu
18 changes: 12 additions & 6 deletions components/eppp_link/README.md
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ We usually call this node a SLAVE microcontroller. The "HOST" microcontroller ru
brings in the WiFi connectivity from the "SLAVE" microcontroller.

```
SLAVE micro HOST micro
\|/ +----------------+ +----------------+
| | | serial line | |
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO =====| PPPoS client |
| (server)| | |
+----------------+ +----------------+
SLAVE micro HOST micro
\|/ +----------------+ +----------------+
| | | (serial) line | |
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO / ETH ===| PPPoS client |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to improve the README with instruction how to use EMAC to EMAC. Or at least provide link to the component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll improve the docs and add sections for each transport.

| (server)| | |
+----------------+ +----------------+
```

## API
@@ -55,3 +55,9 @@ Tested with WiFi-NAPT example

* TCP - 9Mbits/s
* UDP - 11Mbits/s

### Ethernet

- Internal EMAC with real PHY chip
* TCP - 5Mbits/s
* UDP - 8Mbits/s
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm quite surprised with such poor performance. Do you have idea what could be the bottle neck and why it isn't at least as good as SDIO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would probably get similar performance as SDIO, after some optimizations; tested just briefly with all defaults.
I'm planning on looking into it at some point.

120 changes: 120 additions & 0 deletions components/eppp_link/eppp_eth.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_check.h"
#include "esp_event.h"
#include "esp_mac.h"
#include "eppp_link.h"
#include "eppp_transport.h"
#include "esp_eth_driver.h"
#include "ethernet_init.h"
#include "esp_eth_spec.h"

typedef struct header {
uint8_t dst[ETH_ADDR_LEN];
uint8_t src[ETH_ADDR_LEN];
uint16_t len;
} __attribute__((packed)) header_t;

static const char *TAG = "eppp_ethernet";
static bool s_is_connected = false;
static esp_eth_handle_t *s_eth_handles = NULL;
static uint8_t s_out_buffer[ETH_MAX_PACKET_SIZE];
static uint8_t s_their_mac[ETH_ADDR_LEN];
static uint8_t s_our_mac[ETH_ADDR_LEN];

static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
s_is_connected = true;
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
s_is_connected = false;
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}

static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void *netif)
{
header_t *head = (header_t *)buffer;
size_t packet_len = head->len;
if (len >= packet_len) {
esp_err_t ret = esp_netif_receive(netif, buffer + ETH_HEADER_LEN, packet_len, NULL);
free(buffer);
return ret;
}
return ESP_FAIL;
}

__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle_array[])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case here for having the init function as a customization point?

{
uint8_t eth_port_cnt = 0;
ESP_RETURN_ON_ERROR(ethernet_init_all(handle_array, &eth_port_cnt), TAG, "Failed to init common eth drivers");
ESP_RETURN_ON_FALSE(eth_port_cnt > 1, ESP_ERR_INVALID_ARG, TAG, "multiple Ethernet devices detected, please init only one");
return ESP_OK;
}

__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array)
{
ethernet_deinit_all(s_eth_handles);
}


esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
{
ESP_RETURN_ON_ERROR(eppp_transport_ethernet_init(&s_eth_handles), TAG, "Failed to initialize Ethernet driver");
ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback");
sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
&s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]);

sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
&s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]);
esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac);
ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we open space for a custom event handler?

ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver");
return ESP_OK;
}

void eppp_transport_deinit(void)
{
esp_eth_stop(s_eth_handles[0]);
eppp_transport_ethernet_deinit(s_eth_handles);
}

esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
{
if (!s_is_connected) {
return ESP_OK;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a failure?

}
// setup Ethernet header
header_t *head = (header_t *)s_out_buffer;
memcpy(head->dst, s_their_mac, ETH_ADDR_LEN);
memcpy(head->src, s_our_mac, ETH_ADDR_LEN);
head->len = len;
// handle frame size: ETH_MIN_PAYLOAD_LEN <= len <= ETH_MAX_PAYLOAD_LEN
size_t frame_payload_len = len < ETH_MIN_PAYLOAD_LEN ? ETH_MIN_PAYLOAD_LEN : len;
if (frame_payload_len > ETH_MAX_PAYLOAD_LEN) {
return ESP_FAIL;
}
memcpy(s_out_buffer + ETH_HEADER_LEN, buffer, len);
return esp_eth_transmit(s_eth_handles[0], s_out_buffer, frame_payload_len + ETH_HEADER_LEN);
}
26 changes: 24 additions & 2 deletions components/eppp_link/eppp_link.c
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
#include "esp_event.h"
#include "esp_netif_ppp.h"
#include "eppp_link.h"
#include "esp_serial_slave_link/essl_sdio.h"
#include "eppp_transport.h"

#if CONFIG_EPPP_LINK_DEVICE_SPI
#include "driver/spi_master.h"
@@ -24,6 +24,12 @@
#include "driver/uart.h"
#endif

#if CONFIG_EPPP_LINK_DEVICE_ETH
#define EPPP_NEEDS_TASK 0
#else
#define EPPP_NEEDS_TASK 1
#endif

static const int GOT_IPV4 = BIT0;
static const int CONNECTION_FAILED = BIT1;
#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED)
@@ -98,6 +104,8 @@ esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config);
esp_err_t eppp_sdio_slave_init(void);
void eppp_sdio_slave_deinit(void);
void eppp_sdio_host_deinit(void);
#elif CONFIG_EPPP_LINK_DEVICE_ETH

#else
static esp_err_t transmit(void *h, void *buffer, size_t len)
{
@@ -224,6 +232,8 @@ static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config)
.handle = h,
#if CONFIG_EPPP_LINK_DEVICE_SDIO
.transmit = role == EPPP_CLIENT ? eppp_sdio_host_tx : eppp_sdio_slave_tx,
#elif CONFIG_EPPP_LINK_DEVICE_ETH
.transmit = eppp_transport_tx,
#else
.transmit = transmit,
#endif
@@ -691,6 +701,7 @@ esp_err_t eppp_perform(esp_netif_t *netif)

#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART

#if EPPP_NEEDS_TASK
static void ppp_task(void *args)
{
esp_netif_t *netif = args;
@@ -699,6 +710,7 @@ static void ppp_task(void *args)
h->exited = true;
vTaskDelete(NULL);
}
#endif

static bool have_some_eppp_netif(esp_netif_t *netif, void *ctx)
{
@@ -738,6 +750,8 @@ void eppp_deinit(esp_netif_t *netif)
} else {
eppp_sdio_slave_deinit();
}
#elif CONFIG_EPPP_LINK_DEVICE_ETH
eppp_transport_deinit();
#endif
netif_deinit(netif);
}
@@ -781,6 +795,13 @@ esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config)
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
return NULL;
}
#elif CONFIG_EPPP_LINK_DEVICE_ETH
esp_err_t ret = eppp_transport_init(config, netif);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
return NULL;
}

#endif
return netif;
}
@@ -834,12 +855,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time
}

eppp_netif_start(netif);

#if EPPP_NEEDS_TASK
if (xTaskCreate(ppp_task, "ppp connect", config->task.stack_size, netif, config->task.priority, NULL) != pdTRUE) {
ESP_LOGE(TAG, "Failed to create a ppp connection task");
eppp_deinit(netif);
return NULL;
}
#endif
int netif_cnt = get_netif_num(netif);
if (netif_cnt < 0) {
eppp_close(netif);
14 changes: 14 additions & 0 deletions components/eppp_link/eppp_transport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif);

esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len);

esp_err_t eppp_transport_rx(esp_netif_t *netif);

void eppp_transport_deinit(void);
7 changes: 7 additions & 0 deletions components/eppp_link/examples/emac2emac/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)


include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pppos_host)
12 changes: 12 additions & 0 deletions components/eppp_link/examples/emac2emac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

# EPPP link with EMAC to EMAC transport layer

This example runs a symmetrical server-client eppp application with iperf component using Ethernet transport layer, with a customized Ethernet driver from [eth_dummy_phy](https://components.espressif.com/components/espressif/eth_dummy_phy) component.

Please refer to the component documentation for more information about the principle of operation and the actual physical connection between nodes.

## How to use this example

* Choose `CONFIG_EXAMPLE_NODE_SERVER` for the device connected as **RMII CLK Source Device**
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some advice here regarding clk source.
@kostaond the conditions for clk generation if Wi-Fi is used in the device applies here as well, right?

* Choose `EXAMPLE_NODE_CLIENT` for the device connected as **RMII CLK Sink Device**
* Run `iperf` command on both sides to check the network performance (both server and client iperf role could be used on both devices)
2 changes: 2 additions & 0 deletions components/eppp_link/examples/emac2emac/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS app_main.c register_iperf.c
INCLUDE_DIRS ".")
28 changes: 28 additions & 0 deletions components/eppp_link/examples/emac2emac/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
menu "Example Configuration"

choice EXAMPLE_NODE_ROLE
prompt "Choose the device role"
default EXAMPLE_NODE_SERVER
help
Select whether this device acts as a server or a client

config EXAMPLE_NODE_SERVER
bool "Server"
help
Act as an EPPP server, source RMII clock

config EXAMPLE_NODE_CLIENT
bool "Client"
help
Act as an EPPP client, use server's clock

endchoice

config EXAMPLE_RMII_CLK_READY_GPIO
int "RMII CLK Sink Device is ready GPIO"
default 4
help
GPIO number at which the "RMII CLK Sink Device" is ready and so the "RMII
CLK Source Device" can continue in its Ethernet initialization.

endmenu
201 changes: 201 additions & 0 deletions components/eppp_link/examples/emac2emac/main/app_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "eppp_link.h"
#include "esp_log.h"
#include "esp_check.h"
#include "console_ping.h"
#include "esp_eth_driver.h"
#include "driver/gpio.h"
#include "esp_eth_phy_dummy.h"

#ifdef CONFIG_EXAMPLE_NODE_SERVER
#define EPPP_CONFIG() EPPP_DEFAULT_SERVER_CONFIG()
#define EPPP_ROLE EPPP_SERVER
#define EPPP_RMII_CLK_SINK
#else
#define EPPP_CONFIG() EPPP_DEFAULT_CLIENT_CONFIG()
#define EPPP_ROLE EPPP_CLIENT
#define EPPP_RMII_CLK_SOURCE
#endif

void register_iperf(void);

static const char *TAG = "eppp_emac2emac";


static esp_eth_mac_t *s_mac = NULL;
static esp_eth_phy_t *s_phy = NULL;

#ifdef EPPP_RMII_CLK_SOURCE
IRAM_ATTR static void gpio_isr_handler(void *arg)
{
BaseType_t high_task_wakeup = pdFALSE;
TaskHandle_t task_handle = (TaskHandle_t)arg;

vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
if (high_task_wakeup != pdFALSE) {
portYIELD_FROM_ISR();
}
}
#else
#define STARTUP_DELAY_MS 500
#endif

esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle[])
{
*handle = malloc(sizeof(esp_eth_handle_t));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't esp_eth_handle_t a type alias for void*? Do we need to allocate here?

ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "Our of memory");

#ifdef EPPP_RMII_CLK_SOURCE
esp_rom_gpio_pad_select_gpio(EMAC_CLK_OUT_180_GPIO);
gpio_set_pull_mode(EMAC_CLK_OUT_180_GPIO, GPIO_FLOATING); // to not affect GPIO0 (so the Sink Device could be flashed)
gpio_install_isr_service(0);
gpio_config_t gpio_source_cfg = {
.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_RMII_CLK_READY_GPIO),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
.intr_type = GPIO_INTR_ANYEDGE
};
gpio_config(&gpio_source_cfg);
TaskHandle_t task_handle = xTaskGetHandle(pcTaskGetName(NULL));
gpio_isr_handler_add(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, gpio_isr_handler, task_handle);
ESP_LOGW(TAG, "waiting for RMII CLK sink device interrupt");
ESP_LOGW(TAG, "if RMII CLK sink device is already running, reset it by `EN` button");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make it part of the source device to automatically reset the device if the user enables it?

while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (gpio_get_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO) == 1) {
break;
}
}
ESP_LOGI(TAG, "starting Ethernet initialization");
#else
gpio_config_t gpio_sink_cfg = {
.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_RMII_CLK_READY_GPIO),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&gpio_sink_cfg);
gpio_set_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, 0);
vTaskDelay(pdMS_TO_TICKS(STARTUP_DELAY_MS));
gpio_set_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, 1);
#endif // EPPP_RMII_CLK_SOURCE

// --- Initialize Ethernet driver ---
// Init common MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();

// Update PHY config based on board specific configuration
phy_config.reset_gpio_num = -1; // no HW reset

// Init vendor specific MAC config to default
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
// Update vendor specific MAC config based on board configuration
// No SMI, speed/duplex must be statically configured the same in both devices
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
esp32_emac_config.smi_gpio.mdc_num = -1;
esp32_emac_config.smi_gpio.mdio_num = -1;
#else
esp32_emac_config.smi_mdc_gpio_num = -1;
esp32_emac_config.smi_mdio_gpio_num = -1;
#endif
#ifdef EPPP_RMII_CLK_SOURCE
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT;
esp32_emac_config.clock_config.rmii.clock_gpio = EMAC_CLK_OUT_180_GPIO;
#else
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_EXT_IN;
esp32_emac_config.clock_config.rmii.clock_gpio = EMAC_CLK_IN_GPIO;
#endif // EPPP_RMII_CLK_SOURCE

// Create new ESP32 Ethernet MAC instance
s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
// Create dummy PHY instance
s_phy = esp_eth_phy_new_dummy(&phy_config);

// Init Ethernet driver to default and install it
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
#ifdef EPPP_RMII_CLK_SINK
// REF RMII CLK sink device performs multiple EMAC init attempts since RMII CLK source device may not be ready yet
int i;
for (i = 1; i <= 5; i++) {
ESP_LOGI(TAG, "Ethernet driver install attempt: %i", i);
if (esp_eth_driver_install(&config, *handle) == ESP_OK) {
break;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
ESP_RETURN_ON_FALSE(i <= 5, ESP_FAIL, TAG, "Ethernet driver install failed");
#else
ESP_RETURN_ON_ERROR(esp_eth_driver_install(&config, *handle), TAG, "Ethernet driver install failed");
#endif // EPPP_RMII_CLK_SINK
return ESP_OK;

}

void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());

ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());

/* Sets up the default EPPP-connection
*/
eppp_config_t config = EPPP_CONFIG();
config.transport = EPPP_TRANSPORT_ETHERNET;
esp_netif_t *eppp_netif = eppp_open(EPPP_ROLE, &config, portMAX_DELAY);
if (eppp_netif == NULL) {
ESP_LOGE(TAG, "Failed to connect");
return ;
}
// Initialize console REPL
ESP_ERROR_CHECK(console_cmd_init());

register_iperf();

printf("\n =======================================================\n");
printf(" | Steps to Test EPPP-emac2emca bandwidth |\n");
printf(" | |\n");
printf(" | 1. Wait for the ESP32 to get an IP |\n");
printf(" | 2. Server: 'iperf -u -s -i 3' (on host) |\n");
printf(" | 3. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
printf(" | |\n");
printf(" =======================================================\n\n");

// using also ping command to check basic network connectivity
ESP_ERROR_CHECK(console_cmd_ping_register());
ESP_ERROR_CHECK(console_cmd_start());

// handle GPIO0 workaround for ESP32
#ifdef CONFIG_EXAMPLE_NODE_SERVER
// Wait indefinitely or reset when "RMII CLK Sink Device" resets
// We reset the "RMII CLK Source Device" to ensure there is no CLK at GPIO0 of the
// "RMII CLK Sink Device" during startup
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (gpio_get_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO) == 0) {
break;
}
}
ESP_LOGW(TAG, "RMII CLK Sink device reset, I'm going to reset too!");
esp_restart();
#endif
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dependencies:
espressif/iperf-cmd: "^0.1.1"
espressif/eppp_link:
version: "*"
override_path: "../../.."
console_cmd_ping: "*"
espressif/eth_dummy_phy: "*"
179 changes: 179 additions & 0 deletions components/eppp_link/examples/emac2emac/main/register_iperf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "sys/socket.h" // for INADDR_ANY
#include "esp_netif.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_ppp.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"

#include "esp_console.h"
#include "esp_event.h"
#include "esp_bit_defs.h"
#include "argtable3/argtable3.h"
#include "iperf.h"
#include "sdkconfig.h"

/* "iperf" command */

static struct {
struct arg_str *ip;
struct arg_lit *server;
struct arg_lit *udp;
struct arg_lit *version;
struct arg_int *port;
struct arg_int *length;
struct arg_int *interval;
struct arg_int *time;
struct arg_int *bw_limit;
struct arg_lit *abort;
struct arg_end *end;
} iperf_args;

static int ppp_cmd_iperf(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
// ethernet iperf only support IPV4 address
iperf_cfg_t cfg = {.type = IPERF_IP_TYPE_IPV4};

if (nerrors != 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 0;
}

/* iperf -a */
if (iperf_args.abort->count != 0) {
iperf_stop();
return 0;
}

if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
return 0;
}

/* iperf -s */
if (iperf_args.ip->count == 0) {
cfg.flag |= IPERF_FLAG_SERVER;
}
/* iperf -c SERVER_ADDRESS */
else {
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
cfg.flag |= IPERF_FLAG_CLIENT;
}

if (iperf_args.length->count == 0) {
cfg.len_send_buf = 0;
} else {
cfg.len_send_buf = iperf_args.length->ival[0];
}

cfg.source_ip4 = INADDR_ANY;

/* iperf -u */
if (iperf_args.udp->count == 0) {
cfg.flag |= IPERF_FLAG_TCP;
} else {
cfg.flag |= IPERF_FLAG_UDP;
}

/* iperf -p */
if (iperf_args.port->count == 0) {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = IPERF_DEFAULT_PORT;
} else {
if (cfg.flag & IPERF_FLAG_SERVER) {
cfg.sport = iperf_args.port->ival[0];
cfg.dport = IPERF_DEFAULT_PORT;
} else {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = iperf_args.port->ival[0];
}
}

/* iperf -i */
if (iperf_args.interval->count == 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
} else {
cfg.interval = iperf_args.interval->ival[0];
if (cfg.interval <= 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
}
}

/* iperf -t */
if (iperf_args.time->count == 0) {
cfg.time = IPERF_DEFAULT_TIME;
} else {
cfg.time = iperf_args.time->ival[0];
if (cfg.time <= cfg.interval) {
cfg.time = cfg.interval;
}
}

/* iperf -b */
if (iperf_args.bw_limit->count == 0) {
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
} else {
cfg.bw_lim = iperf_args.bw_limit->ival[0];
if (cfg.bw_lim <= 0) {
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
}
}

printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n",
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
(uint16_t) cfg.source_ip4 & 0xFF,
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
cfg.sport,
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
cfg.interval, cfg.time);

iperf_start(&cfg);
return 0;
}

void register_iperf(void)
{

iperf_args.ip = arg_str0("c", "client", "<ip>",
"run in client mode, connecting to <host>");
iperf_args.server = arg_lit0("s", "server", "run in server mode");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
iperf_args.port = arg_int0("p", "port", "<port>",
"server port to listen on/connect to");
iperf_args.length = arg_int0("l", "len", "<length>", "set read/write buffer size");
iperf_args.interval = arg_int0("i", "interval", "<interval>",
"seconds between periodic bandwidth reports");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
iperf_args.end = arg_end(1);
const esp_console_cmd_t iperf_cmd = {
.command = "iperf",
.help = "iperf command",
.hint = NULL,
.func = &ppp_cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
}
3 changes: 3 additions & 0 deletions components/eppp_link/examples/emac2emac/sdkconfig.ci.client
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_EXAMPLE_NODE_CLIENT=y
CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS="06:00:00:00:00:02"
CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS="06:00:00:00:00:01"
3 changes: 3 additions & 0 deletions components/eppp_link/examples/emac2emac/sdkconfig.ci.server
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_EXAMPLE_NODE_SERVER=y
CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS="06:00:00:00:00:01"
CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS="06:00:00:00:00:02"
6 changes: 6 additions & 0 deletions components/eppp_link/examples/emac2emac/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
CONFIG_LWIP_PPP_DEBUG_ON=y
CONFIG_EPPP_LINK_DEVICE_ETH=y
2 changes: 2 additions & 0 deletions components/eppp_link/examples/host/sdkconfig.ci.eth
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y
2 changes: 2 additions & 0 deletions components/eppp_link/examples/host/sdkconfig.ci.sdio
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_EPPP_LINK_DEVICE_SDIO=y
2 changes: 2 additions & 0 deletions components/eppp_link/examples/slave/sdkconfig.ci.eth
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y
2 changes: 2 additions & 0 deletions components/eppp_link/examples/slave/sdkconfig.ci.sdio
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32c6"
CONFIG_EPPP_LINK_DEVICE_SDIO=y
1 change: 1 addition & 0 deletions components/eppp_link/idf_component.yml
Original file line number Diff line number Diff line change
@@ -4,3 +4,4 @@ description: The component provides a general purpose PPP connectivity, typicall
dependencies:
idf: '>=5.2'
espressif/esp_serial_slave_link: "^1.1.0"
espressif/ethernet_init: '>=0.0.7'
1 change: 1 addition & 0 deletions components/eppp_link/include/eppp_link.h
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ typedef enum eppp_transport {
EPPP_TRANSPORT_UART,
EPPP_TRANSPORT_SPI,
EPPP_TRANSPORT_SDIO,
EPPP_TRANSPORT_ETHERNET,
} eppp_transport_t;