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
Loading