Skip to content

Commit

Permalink
feat(eppp): Add support for transport via Ethernet EMAC-2-EMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
david-cermak committed Jul 25, 2024
1 parent 776e40c commit 150f1b3
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 8 deletions.
30 changes: 30 additions & 0 deletions components/eppp_link/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,34 @@ menu "eppp_link"
default "06:00:00:00:00:02"
depends on EPPP_LINK_DEVICE_ETH

config EPPP_LINK_ETHERNET_USE_DUMMY_PHY
bool "Use dummy phy (emac2emac)"
default n
depends on EPPP_LINK_DEVICE_ETH

choice EPPP_LINK_ETHERNET_CLK_ROLE
prompt "RMII CLK Role"
default EPPP_LINK_ETHERNET_CLK_SOURCE
depends on EPPP_LINK_ETHERNET_USE_DUMMY_PHY
help
Configure device type. ESP32 device which is RMII CLK source needs to wait
with its Ethernet initialization for the "RMII CLK Sink Device" since
the RMII CLK input pin (GPIO0) is also used as a boot strap pin. If
the "RMII CLK Source Device" didn't wait, the "RMII CLK Sink Device"
could boot into incorrect mode.

config EPPP_LINK_ETHERNET_CLK_SOURCE
bool "RMII CLK Source Device"
config EPPP_LINK_ETHERNET_CLK_SINK
bool "RMII CLK Sink Device"
endchoice

config EPPP_LINK_ETHERNET_SINK_READY_GPIO
int "RMII CLK Sink Device is ready GPIO"
default 4
depends on EPPP_LINK_ETHERNET_USE_DUMMY_PHY
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
161 changes: 153 additions & 8 deletions components/eppp_link/eppp_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "esp_eth_driver.h"
#include "ethernet_init.h"
#include "esp_eth_spec.h"
#include "driver/gpio.h"
#include "esp_eth_phy_dummy.h"

typedef struct header {
uint8_t dst[ETH_ADDR_LEN];
Expand Down Expand Up @@ -65,28 +67,32 @@ static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void
return ESP_FAIL;
}

static esp_err_t eth_init(void);

esp_err_t eppp_transport_init(eppp_config_t *config, esp_netif_t *esp_netif)
{
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(eth_init(), 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;
}

static void emac2emac_deinit(void);

void eppp_transport_deinit(void)
{
#ifdef CONFIG_EPPP_LINK_ETHERNET_USE_DUMMY_PHY
emac2emac_deinit();
#else
ethernet_deinit_all(s_eth_handles);
#endif
}

esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
Expand All @@ -107,3 +113,142 @@ esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
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);
}

#ifdef CONFIG_EPPP_LINK_ETHERNET_USE_DUMMY_PHY

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

#if CONFIG_EPPP_LINK_ETHERNET_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

static esp_err_t eth_init(void)
{
#if CONFIG_EPPP_LINK_ETHERNET_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_EPPP_LINK_ETHERNET_SINK_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_EPPP_LINK_ETHERNET_SINK_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_EPPP_LINK_ETHERNET_SINK_READY_GPIO) == 1) {
break;
}
}
ESP_LOGI(TAG, "starting Ethernet initialization");
#else
gpio_config_t gpio_sink_cfg = {
.pin_bit_mask = (1ULL << CONFIG_EPPP_LINK_ETHERNET_SINK_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_EPPP_LINK_ETHERNET_SINK_READY_GPIO, 0);
vTaskDelay(pdMS_TO_TICKS(STARTUP_DELAY_MS));
gpio_set_level(CONFIG_EPPP_LINK_ETHERNET_SINK_READY_GPIO, 1);
#endif // CONFIG_EPPP_LINK_ETHERNET_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
#if CONFIG_EPPP_LINK_ETHERNET_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 // CONFIG_EPPP_LINK_ETHERNET_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
s_eth_handles = malloc(sizeof(esp_eth_handle_t));
ESP_RETURN_ON_FALSE(s_eth_handles, ESP_ERR_NO_MEM, TAG, "Our of memory");
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
#if CONFIG_EPPP_LINK_ETHERNET_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, &s_eth_handles[0]) == 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, &s_eth_handles[0]), TAG, "Ethernet driver install failed");
#endif // CONFIG_EPPP_LINK_ETHERNET_CLK_SINK
return ESP_OK;
}

static void emac2emac_deinit(void)
{
if (esp_eth_driver_uninstall(&s_eth_handles[0]) != ESP_OK) {
ESP_LOGE(TAG, "Unable to deinitialize ethernet handle");
}
if (s_mac) {
s_mac->del(s_mac);
s_mac = NULL;
}
if (s_phy) {
s_phy->del(s_phy);
s_phy = NULL;
}
free(s_eth_handles);
s_eth_handles = NULL;
}

#else
static esp_err_t eth_init(void)
{
uint8_t eth_port_cnt = 0;
ESP_RETURN_ON_ERROR(ethernet_init_all(&s_eth_handles, &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;
}
#endif // CONFIG_EPPP_LINK_ETHERNET_USE_DUMMY_PHY
4 changes: 4 additions & 0 deletions components/eppp_link/examples/host/sdkconfig.ci.emac2emac
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=y
CONFIG_EPPP_LINK_ETHERNET_USE_DUMMY_PHY=y
CONFIG_EPPP_LINK_ETHERNET_CLK_SOURCE=y
6 changes: 6 additions & 0 deletions components/eppp_link/examples/slave/sdkconfig.ci.emac2emac
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_EPPP_LINK_DEVICE_ETH=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"
CONFIG_EPPP_LINK_ETHERNET_USE_DUMMY_PHY=y
CONFIG_EPPP_LINK_ETHERNET_CLK_SINK=y
1 change: 1 addition & 0 deletions components/eppp_link/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dependencies:
idf: '>=5.2'
espressif/esp_serial_slave_link: "^1.1.0"
espressif/ethernet_init: '>=0.0.7'
espressif/eth_dummy_phy: '*'

0 comments on commit 150f1b3

Please sign in to comment.