-
Notifications
You must be signed in to change notification settings - Fork 149
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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() |
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 | | ||
| (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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
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[]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, ð_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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} |
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); |
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) |
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** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add some advice here regarding clk source. |
||
* 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) |
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 ".") |
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 |
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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn'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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: "*" |
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)); | ||
} |
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" |
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" |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CONFIG_IDF_TARGET="esp32" | ||
CONFIG_EPPP_LINK_DEVICE_ETH=y |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CONFIG_IDF_TARGET="esp32p4" | ||
CONFIG_EPPP_LINK_DEVICE_SDIO=y |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CONFIG_IDF_TARGET="esp32" | ||
CONFIG_EPPP_LINK_DEVICE_ETH=y |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CONFIG_IDF_TARGET="esp32c6" | ||
CONFIG_EPPP_LINK_DEVICE_SDIO=y |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.