esp32/network_lan: Add Ethernet support for IDF v4.1 and above.
Ethernet-PHYs from ESP-IDF (LAN8720, IP101, RTL8201, DP83848) are now
supported in IDF v4.1 and above. PHY_KSZ8041 is only for ESP-IDF 4.3 and
above. ESP32S2 is not supported.
Signed-off-by: Tobias Eydam <eydam-prototyping@outlook.com>
diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c
index 7a4ad49..34e5cba 100644
--- a/ports/esp32/network_lan.c
+++ b/ports/esp32/network_lan.c
@@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2017 "Eric Poulsen" <eric@zyxod.com>
+ * Copyright (c) 2021 "Tobias Eydam" <tobiaseydam@hotmail.com>
*
* Based on the ESP IDF example code which is Public Domain / CC0
*
@@ -26,15 +27,19 @@
* THE SOFTWARE.
*/
-#if !MICROPY_ESP_IDF_4
#include "py/runtime.h"
#include "py/mphal.h"
-#include "eth_phy/phy.h"
-#include "eth_phy/phy_tlk110.h"
-#include "eth_phy/phy_lan8720.h"
-#include "eth_phy/phy_ip101.h"
-#include "tcpip_adapter.h"
+#include "esp_idf_version.h"
+
+// LAN only for ESP32 (not ESP32S2) and only for ESP-IDF v4.1 and higher
+#if (ESP_IDF_VERSION_MAJOR == 4) && (ESP_IDF_VERSION_MINOR >= 1) && (CONFIG_IDF_TARGET_ESP32)
+
+#include "esp_eth.h"
+#include "esp_eth_mac.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "esp_netif.h"
#include "modnetwork.h"
@@ -48,47 +53,43 @@
int8_t phy_power_pin;
uint8_t phy_addr;
uint8_t phy_type;
- eth_phy_check_link_func link_func;
- eth_phy_power_enable_func power_func;
+ esp_eth_phy_t *phy;
+ esp_netif_t *eth_netif;
+ esp_eth_handle_t eth_handle;
} lan_if_obj_t;
const mp_obj_type_t lan_if_type;
STATIC lan_if_obj_t lan_obj = {{&lan_if_type}, ESP_IF_ETH, false, false};
+STATIC uint8_t eth_status = 0;
-STATIC void phy_power_enable(bool enable) {
- lan_if_obj_t *self = &lan_obj;
-
- if (self->phy_power_pin != -1) {
-
- if (!enable) {
- // Do the PHY-specific power_enable(false) function before powering down
- self->power_func(false);
- }
-
- gpio_pad_select_gpio(self->phy_power_pin);
- gpio_set_direction(self->phy_power_pin, GPIO_MODE_OUTPUT);
- if (enable) {
- gpio_set_level(self->phy_power_pin, 1);
- } else {
- gpio_set_level(self->phy_power_pin, 0);
- }
-
- // Allow the power up/down to take effect, min 300us
- vTaskDelay(1);
-
- if (enable) {
- // Run the PHY-specific power on operations now the PHY has power
- self->power_func(true);
- }
+static void eth_event_handler(void *arg, esp_event_base_t event_base,
+ int32_t event_id, void *event_data) {
+ switch (event_id) {
+ case ETHERNET_EVENT_CONNECTED:
+ eth_status = ETH_CONNECTED;
+ ESP_LOGI("ethernet", "Ethernet Link Up");
+ break;
+ case ETHERNET_EVENT_DISCONNECTED:
+ eth_status = ETH_DISCONNECTED;
+ ESP_LOGI("ethernet", "Ethernet Link Down");
+ break;
+ case ETHERNET_EVENT_START:
+ eth_status = ETH_STARTED;
+ ESP_LOGI("ethernet", "Ethernet Started");
+ break;
+ case ETHERNET_EVENT_STOP:
+ eth_status = ETH_STOPPED;
+ ESP_LOGI("ethernet", "Ethernet Stopped");
+ break;
+ case IP_EVENT_ETH_GOT_IP:
+ eth_status = ETH_GOT_IP;
+ ESP_LOGI("ethernet", "Ethernet Got IP");
+ break;
+ default:
+ break;
}
}
-STATIC void init_lan_rmii() {
- lan_if_obj_t *self = &lan_obj;
- phy_rmii_configure_data_interface_pins();
- phy_rmii_smi_configure_pins(self->mdc_pin, self->mdio_pin);
-}
-
STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
lan_if_obj_t *self = &lan_obj;
@@ -96,7 +97,7 @@
return MP_OBJ_FROM_PTR(&lan_obj);
}
- enum { ARG_id, ARG_mdc, ARG_mdio, ARG_power, ARG_phy_addr, ARG_phy_type, ARG_clock_mode };
+ enum { ARG_id, ARG_mdc, ARG_mdio, ARG_power, ARG_phy_addr, ARG_phy_type };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_mdc, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
@@ -104,7 +105,6 @@
{ MP_QSTR_power, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_phy_addr, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
- { MP_QSTR_clock_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@@ -123,56 +123,91 @@
if (args[ARG_phy_addr].u_int < 0x00 || args[ARG_phy_addr].u_int > 0x1f) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid phy address"));
}
+ self->phy_addr = args[ARG_phy_addr].u_int;
if (args[ARG_phy_type].u_int != PHY_LAN8720 &&
- args[ARG_phy_type].u_int != PHY_TLK110 &&
- args[ARG_phy_type].u_int != PHY_IP101) {
+ args[ARG_phy_type].u_int != PHY_IP101 &&
+ args[ARG_phy_type].u_int != PHY_RTL8201 &&
+ #if ESP_IDF_VERSION_MINOR >= 3 // KSZ8041 is new in ESP-IDF v4.3
+ args[ARG_phy_type].u_int != PHY_KSZ8041 &&
+ #endif
+ args[ARG_phy_type].u_int != PHY_DP83848) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid phy type"));
}
- if (args[ARG_clock_mode].u_int != -1 &&
- args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO0_IN &&
- // Disabled due ESP-IDF (see modnetwork.c note)
- // args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO0_OUT &&
- args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO16_OUT &&
- args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO17_OUT) {
- mp_raise_ValueError(MP_ERROR_TEXT("invalid clock mode"));
- }
+ eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
+ mac_config.smi_mdc_gpio_num = self->mdc_pin;
+ mac_config.smi_mdio_gpio_num = self->mdio_pin;
+ esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
- eth_config_t config;
+ eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
+ phy_config.phy_addr = self->phy_addr;
+ phy_config.reset_gpio_num = self->phy_power_pin;
+ self->phy = NULL;
switch (args[ARG_phy_type].u_int) {
- case PHY_TLK110:
- config = phy_tlk110_default_ethernet_config;
- break;
case PHY_LAN8720:
- config = phy_lan8720_default_ethernet_config;
+ self->phy = esp_eth_phy_new_lan8720(&phy_config);
break;
case PHY_IP101:
- config = phy_ip101_default_ethernet_config;
+ self->phy = esp_eth_phy_new_ip101(&phy_config);
break;
+ case PHY_RTL8201:
+ self->phy = esp_eth_phy_new_rtl8201(&phy_config);
+ break;
+ case PHY_DP83848:
+ self->phy = esp_eth_phy_new_dp83848(&phy_config);
+ break;
+ case PHY_KSZ8041:
+ #if ESP_IDF_VERSION_MINOR >= 3 // KSZ8041 is new in ESP-IDF v4.3
+ self->phy = esp_eth_phy_new_ksz8041(&phy_config);
+ break;
+ #endif
+ default:
+ mp_raise_ValueError(MP_ERROR_TEXT("unknown phy"));
}
- self->link_func = config.phy_check_link;
-
- // Replace default power func with our own
- self->power_func = config.phy_power_enable;
- config.phy_power_enable = phy_power_enable;
-
- config.phy_addr = args[ARG_phy_addr].u_int;
- config.gpio_config = init_lan_rmii;
- config.tcpip_input = tcpip_adapter_eth_input;
-
- if (args[ARG_clock_mode].u_int != -1) {
- config.clock_mode = args[ARG_clock_mode].u_int;
+ if (esp_netif_init() != ESP_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_netif_init failed"));
}
- if (esp_eth_init(&config) == ESP_OK) {
+ esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
+ self->eth_netif = esp_netif_new(&cfg);
+
+ if (esp_eth_set_default_handlers(self->eth_netif) != ESP_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_set_default_handlers failed (invalid parameter)"));
+ }
+
+ if (esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL) != ESP_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_event_handler_register failed"));
+ }
+
+ if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL) != ESP_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_event_handler_register failed"));
+ }
+
+ esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, self->phy);
+
+ esp_err_t esp_err = esp_eth_driver_install(&config, &self->eth_handle);
+ if (esp_err == ESP_OK) {
self->active = false;
self->initialized = true;
} else {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_init() failed"));
+ if (esp_err == ESP_ERR_INVALID_ARG) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed with invalid argument"));
+ } else if (esp_err == ESP_ERR_NO_MEM) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed with no memory for driver"));
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed"));
+ }
}
+
+ if (esp_netif_attach(self->eth_netif, esp_eth_new_netif_glue(self->eth_handle)) != ESP_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_netif_attach failed"));
+ }
+
+ eth_status = ETH_INITIALIZED;
+
return MP_OBJ_FROM_PTR(&lan_obj);
}
MP_DEFINE_CONST_FUN_OBJ_KW(get_lan_obj, 0, get_lan);
@@ -182,37 +217,87 @@
if (n_args > 1) {
if (mp_obj_is_true(args[1])) {
- self->active = (esp_eth_enable() == ESP_OK);
+ self->active = (esp_eth_start(self->eth_handle) == ESP_OK);
if (!self->active) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet enable failed"));
}
} else {
- self->active = !(esp_eth_disable() == ESP_OK);
+ self->active = !(esp_eth_stop(self->eth_handle) == ESP_OK);
if (self->active) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet disable failed"));
}
}
}
+
return mp_obj_new_bool(self->active);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lan_active_obj, 1, 2, lan_active);
STATIC mp_obj_t lan_status(mp_obj_t self_in) {
- return mp_const_none;
+ return MP_OBJ_NEW_SMALL_INT(eth_status);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_status_obj, lan_status);
STATIC mp_obj_t lan_isconnected(mp_obj_t self_in) {
lan_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
- return self->active ? mp_obj_new_bool(self->link_func()) : mp_const_false;
+ return self->active ? mp_obj_new_bool(self->phy->get_link(self->phy) == ETH_LINK_UP) : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_isconnected_obj, lan_isconnected);
+STATIC mp_obj_t lan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+ if (n_args != 1 && kwargs->used != 0) {
+ mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
+ }
+ lan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x)
+
+ if (kwargs->used != 0) {
+
+ for (size_t i = 0; i < kwargs->alloc; i++) {
+ if (mp_map_slot_is_filled(kwargs, i)) {
+ switch ((uintptr_t)kwargs->table[i].key) {
+ case QS(MP_QSTR_mac): {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ);
+ if (bufinfo.len != 6) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length"));
+ }
+ esp_eth_ioctl(self->eth_handle, ETH_CMD_S_MAC_ADDR, bufinfo.buf);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return mp_const_none;
+ }
+
+ if (n_args != 2) {
+ mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
+ }
+
+ mp_obj_t val = mp_const_none;
+
+ switch ((uintptr_t)args[1]) {
+ case QS(MP_QSTR_mac): {
+ uint8_t mac[6];
+ esp_eth_ioctl(self->eth_handle, ETH_CMD_G_MAC_ADDR, mac);
+ return mp_obj_new_bytes(mac, sizeof(mac));
+ }
+ default:
+ mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
+ }
+
+ return val;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(lan_config_obj, 1, lan_config);
+
STATIC const mp_rom_map_elem_t lan_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&lan_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&lan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lan_status_obj) },
- { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) },
+ { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&lan_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) },
};
@@ -224,4 +309,4 @@
.locals_dict = (mp_obj_dict_t *)&lan_if_locals_dict,
};
-#endif // !MICROPY_ESP_IDF_4
+#endif