Skip to content

Commit e03a0cd

Browse files
committed
feat(eppp): Add demo with custom eth-driver using emac2emac
1 parent 1778b90 commit e03a0cd

13 files changed

+470
-10
lines changed

.github/workflows/eppp__build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
idf_ver: ["latest", "release-v5.3"]
17-
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
17+
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: host, path: "examples/emac2emac" }, { app: test_app, path: "test/test_app" }]
1818
runs-on: ubuntu-22.04
1919
container: espressif/idf:${{ matrix.idf_ver }}
2020
steps:

components/eppp_link/eppp_eth.c

+20-9
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,39 @@ static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void
6565
return ESP_FAIL;
6666
}
6767

68-
esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
68+
__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle_array[])
6969
{
7070
uint8_t eth_port_cnt = 0;
71-
ESP_ERROR_CHECK(ethernet_init_all(&s_eth_handles, &eth_port_cnt));
72-
if (eth_port_cnt > 1) {
73-
ESP_LOGW(TAG, "multiple Ethernet devices detected, the first initialized is to be used!");
74-
}
75-
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif));
71+
ESP_RETURN_ON_ERROR(ethernet_init_all(handle_array, &eth_port_cnt), TAG, "Failed to init common eth drivers");
72+
ESP_RETURN_ON_FALSE(eth_port_cnt > 1, ESP_ERR_INVALID_ARG, TAG, "multiple Ethernet devices detected, please init only one");
73+
return ESP_OK;
74+
}
75+
76+
__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array)
77+
{
78+
ethernet_deinit_all(s_eth_handles);
79+
}
80+
81+
82+
esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
83+
{
84+
ESP_RETURN_ON_ERROR(eppp_transport_ethernet_init(&s_eth_handles), TAG, "Failed to initialize Ethernet driver");
85+
ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback");
7686
sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
7787
&s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]);
7888

7989
sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIu8 ":%2" PRIu8 ":%2" PRIi8 ":%2" PRIu8 ":%2" PRIu8 ":%2" PRIu8,
8090
&s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]);
8191
esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac);
82-
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
83-
ESP_ERROR_CHECK(esp_eth_start(s_eth_handles[0]));
92+
ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers");
93+
ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver");
8494
return ESP_OK;
8595
}
8696

8797
void eppp_transport_deinit(void)
8898
{
89-
ethernet_deinit_all(s_eth_handles);
99+
esp_eth_stop(s_eth_handles[0]);
100+
eppp_transport_ethernet_deinit(s_eth_handles);
90101
}
91102

92103
esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# The following four lines of boilerplate have to be in your project's CMakeLists
2+
# in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
6+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
7+
project(pppos_host)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
# EPPP link with EMAC to EMAC transport layer
3+
4+
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.
5+
6+
Please refer to the component documentation for more information about the principle of operation and the actual physical connection between nodes.
7+
8+
## How to use this example
9+
10+
* Choose `CONFIG_EXAMPLE_NODE_SERVER` for the device connected as **RMII CLK Source Device**
11+
* Choose `EXAMPLE_NODE_CLIENT` for the device connected as **RMII CLK Sink Device**
12+
* Run `iperf` command on both sides to check the network performance (both server and client iperf role could be used on both devices)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
idf_component_register(SRCS app_main.c register_iperf.c
2+
INCLUDE_DIRS ".")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
menu "Example Configuration"
2+
3+
choice EXAMPLE_NODE_ROLE
4+
prompt "Choose the device role"
5+
default EXAMPLE_NODE_SERVER
6+
help
7+
Select whether this device acts as a server or a client
8+
9+
config EXAMPLE_NODE_SERVER
10+
bool "Server"
11+
help
12+
Act as an EPPP server, source RMII clock
13+
14+
config EXAMPLE_NODE_CLIENT
15+
bool "Client"
16+
help
17+
Act as an EPPP client, use server's clock
18+
19+
endchoice
20+
21+
config EXAMPLE_RMII_CLK_READY_GPIO
22+
int "RMII CLK Sink Device is ready GPIO"
23+
default 4
24+
help
25+
GPIO number at which the "RMII CLK Sink Device" is ready and so the "RMII
26+
CLK Source Device" can continue in its Ethernet initialization.
27+
28+
endmenu
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
7+
#include <stdio.h>
8+
#include <stdint.h>
9+
#include <stddef.h>
10+
#include <string.h>
11+
#include "esp_system.h"
12+
#include "nvs_flash.h"
13+
#include "esp_event.h"
14+
#include "esp_netif.h"
15+
#include "eppp_link.h"
16+
#include "esp_log.h"
17+
#include "esp_check.h"
18+
#include "console_ping.h"
19+
#include "esp_eth_driver.h"
20+
#include "driver/gpio.h"
21+
#include "esp_eth_phy_dummy.h"
22+
23+
#ifdef CONFIG_EXAMPLE_NODE_SERVER
24+
#define EPPP_CONFIG() EPPP_DEFAULT_SERVER_CONFIG()
25+
#define EPPP_ROLE EPPP_SERVER
26+
#define EPPP_RMII_CLK_SINK
27+
#else
28+
#define EPPP_CONFIG() EPPP_DEFAULT_CLIENT_CONFIG()
29+
#define EPPP_ROLE EPPP_CLIENT
30+
#define EPPP_RMII_CLK_SOURCE
31+
#endif
32+
33+
void register_iperf(void);
34+
35+
static const char *TAG = "eppp_emac2emac";
36+
37+
38+
static esp_eth_mac_t *s_mac = NULL;
39+
static esp_eth_phy_t *s_phy = NULL;
40+
41+
#ifdef EPPP_RMII_CLK_SOURCE
42+
IRAM_ATTR static void gpio_isr_handler(void *arg)
43+
{
44+
BaseType_t high_task_wakeup = pdFALSE;
45+
TaskHandle_t task_handle = (TaskHandle_t)arg;
46+
47+
vTaskNotifyGiveFromISR(task_handle, &high_task_wakeup);
48+
if (high_task_wakeup != pdFALSE) {
49+
portYIELD_FROM_ISR();
50+
}
51+
}
52+
#else
53+
#define STARTUP_DELAY_MS 500
54+
#endif
55+
56+
esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle[])
57+
{
58+
*handle = malloc(sizeof(esp_eth_handle_t));
59+
ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "Our of memory");
60+
61+
#ifdef EPPP_RMII_CLK_SOURCE
62+
esp_rom_gpio_pad_select_gpio(EMAC_CLK_OUT_180_GPIO);
63+
gpio_set_pull_mode(EMAC_CLK_OUT_180_GPIO, GPIO_FLOATING); // to not affect GPIO0 (so the Sink Device could be flashed)
64+
gpio_install_isr_service(0);
65+
gpio_config_t gpio_source_cfg = {
66+
.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_RMII_CLK_READY_GPIO),
67+
.mode = GPIO_MODE_INPUT,
68+
.pull_up_en = GPIO_PULLUP_DISABLE,
69+
.pull_down_en = GPIO_PULLDOWN_ENABLE,
70+
.intr_type = GPIO_INTR_ANYEDGE
71+
};
72+
gpio_config(&gpio_source_cfg);
73+
TaskHandle_t task_handle = xTaskGetHandle(pcTaskGetName(NULL));
74+
gpio_isr_handler_add(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, gpio_isr_handler, task_handle);
75+
ESP_LOGW(TAG, "waiting for RMII CLK sink device interrupt");
76+
ESP_LOGW(TAG, "if RMII CLK sink device is already running, reset it by `EN` button");
77+
while (1) {
78+
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
79+
if (gpio_get_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO) == 1) {
80+
break;
81+
}
82+
}
83+
ESP_LOGI(TAG, "starting Ethernet initialization");
84+
#else
85+
gpio_config_t gpio_sink_cfg = {
86+
.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_RMII_CLK_READY_GPIO),
87+
.mode = GPIO_MODE_OUTPUT,
88+
.pull_up_en = GPIO_PULLUP_DISABLE,
89+
.pull_down_en = GPIO_PULLDOWN_DISABLE,
90+
.intr_type = GPIO_INTR_DISABLE
91+
};
92+
gpio_config(&gpio_sink_cfg);
93+
gpio_set_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, 0);
94+
vTaskDelay(pdMS_TO_TICKS(STARTUP_DELAY_MS));
95+
gpio_set_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO, 1);
96+
#endif // EPPP_RMII_CLK_SOURCE
97+
98+
// --- Initialize Ethernet driver ---
99+
// Init common MAC and PHY configs to default
100+
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
101+
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
102+
103+
// Update PHY config based on board specific configuration
104+
phy_config.reset_gpio_num = -1; // no HW reset
105+
106+
// Init vendor specific MAC config to default
107+
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
108+
// Update vendor specific MAC config based on board configuration
109+
// No SMI, speed/duplex must be statically configured the same in both devices
110+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
111+
esp32_emac_config.smi_gpio.mdc_num = -1;
112+
esp32_emac_config.smi_gpio.mdio_num = -1;
113+
#else
114+
esp32_emac_config.smi_mdc_gpio_num = -1;
115+
esp32_emac_config.smi_mdio_gpio_num = -1;
116+
#endif
117+
#ifdef EPPP_RMII_CLK_SOURCE
118+
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT;
119+
esp32_emac_config.clock_config.rmii.clock_gpio = EMAC_CLK_OUT_180_GPIO;
120+
#else
121+
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_EXT_IN;
122+
esp32_emac_config.clock_config.rmii.clock_gpio = EMAC_CLK_IN_GPIO;
123+
#endif // EPPP_RMII_CLK_SOURCE
124+
125+
// Create new ESP32 Ethernet MAC instance
126+
s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
127+
// Create dummy PHY instance
128+
s_phy = esp_eth_phy_new_dummy(&phy_config);
129+
130+
// Init Ethernet driver to default and install it
131+
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
132+
#ifdef EPPP_RMII_CLK_SINK
133+
// REF RMII CLK sink device performs multiple EMAC init attempts since RMII CLK source device may not be ready yet
134+
int i;
135+
for (i = 1; i <= 5; i++) {
136+
ESP_LOGI(TAG, "Ethernet driver install attempt: %i", i);
137+
if (esp_eth_driver_install(&config, *handle) == ESP_OK) {
138+
break;
139+
}
140+
vTaskDelay(pdMS_TO_TICKS(100));
141+
}
142+
ESP_RETURN_ON_FALSE(i <= 5, ESP_FAIL, TAG, "Ethernet driver install failed");
143+
#else
144+
ESP_RETURN_ON_ERROR(esp_eth_driver_install(&config, *handle), TAG, "Ethernet driver install failed");
145+
#endif // EPPP_RMII_CLK_SINK
146+
return ESP_OK;
147+
148+
}
149+
150+
void app_main(void)
151+
{
152+
ESP_LOGI(TAG, "[APP] Startup..");
153+
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
154+
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
155+
156+
ESP_ERROR_CHECK(nvs_flash_init());
157+
ESP_ERROR_CHECK(esp_netif_init());
158+
ESP_ERROR_CHECK(esp_event_loop_create_default());
159+
160+
/* Sets up the default EPPP-connection
161+
*/
162+
eppp_config_t config = EPPP_CONFIG();
163+
config.transport = EPPP_TRANSPORT_ETHERNET;
164+
esp_netif_t *eppp_netif = eppp_open(EPPP_ROLE, &config, portMAX_DELAY);
165+
if (eppp_netif == NULL) {
166+
ESP_LOGE(TAG, "Failed to connect");
167+
return ;
168+
}
169+
// Initialize console REPL
170+
ESP_ERROR_CHECK(console_cmd_init());
171+
172+
register_iperf();
173+
174+
printf("\n =======================================================\n");
175+
printf(" | Steps to Test EPPP-emac2emca bandwidth |\n");
176+
printf(" | |\n");
177+
printf(" | 1. Wait for the ESP32 to get an IP |\n");
178+
printf(" | 2. Server: 'iperf -u -s -i 3' (on host) |\n");
179+
printf(" | 3. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
180+
printf(" | |\n");
181+
printf(" =======================================================\n\n");
182+
183+
// using also ping command to check basic network connectivity
184+
ESP_ERROR_CHECK(console_cmd_ping_register());
185+
ESP_ERROR_CHECK(console_cmd_start());
186+
187+
// handle GPIO0 workaround for ESP32
188+
#ifdef CONFIG_EXAMPLE_NODE_SERVER
189+
// Wait indefinitely or reset when "RMII CLK Sink Device" resets
190+
// We reset the "RMII CLK Source Device" to ensure there is no CLK at GPIO0 of the
191+
// "RMII CLK Sink Device" during startup
192+
while (1) {
193+
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
194+
if (gpio_get_level(CONFIG_EXAMPLE_RMII_CLK_READY_GPIO) == 0) {
195+
break;
196+
}
197+
}
198+
ESP_LOGW(TAG, "RMII CLK Sink device reset, I'm going to reset too!");
199+
esp_restart();
200+
#endif
201+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dependencies:
2+
espressif/iperf-cmd: "^0.1.1"
3+
espressif/eppp_link:
4+
version: "*"
5+
override_path: "../../.."
6+
console_cmd_ping: "*"
7+
espressif/eth_dummy_phy: "*"

0 commit comments

Comments
 (0)