Skip to content

Commit

Permalink
feat(eppp): Add demo with custom eth-driver using emac2emac
Browse files Browse the repository at this point in the history
  • Loading branch information
david-cermak committed Jul 26, 2024
1 parent 776e40c commit 35f9579
Show file tree
Hide file tree
Showing 13 changed files with 470 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/eppp__build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
29 changes: 20 additions & 9 deletions components/eppp_link/eppp_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,39 @@ static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void
return ESP_FAIL;
}

esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(esp_eth_handle_t *handle_array[])
{
uint8_t eth_port_cnt = 0;
ESP_ERROR_CHECK(ethernet_init_all(&s_eth_handles, &eth_port_cnt));
if (eth_port_cnt > 1) {
ESP_LOGW(TAG, "multiple Ethernet devices detected, the first initialized is to be used!");
}
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif));
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_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
ESP_ERROR_CHECK(esp_eth_start(s_eth_handles[0]));
ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers");
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)
{
ethernet_deinit_all(s_eth_handles);
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)
Expand Down
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**
* 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));
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");
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: "*"
Loading

0 comments on commit 35f9579

Please sign in to comment.