diff --git a/src/core/network.cpp b/src/core/network.cpp index 467171626..70eeeff90 100644 --- a/src/core/network.cpp +++ b/src/core/network.cpp @@ -522,6 +522,22 @@ void Network::startWIFI() { #endif } +#if CONFIG_IDF_TARGET_ESP32 && !defined(EMSESP_STANDALONE) +// Unlike WiFi, which stamps the hostname onto the STA netif as it is created, the Arduino ETH class +// leaves it unset, so lwIP falls back to CONFIG_LWIP_LOCAL_HOSTNAME ("tasmota" in the SDK we build +// against). esp_eth_start() polls the PHY synchronously, so on a warm reboot where the link never +// dropped the DHCP DISCOVER can go out before ETH.begin() has even returned. This handler is +// registered ahead of the esp_netif glue and therefore runs first, applying the hostname before the +// netif is handed to lwIP. +static char eth_hostname[33] = {0}; + +static void ethApplyHostname(void * /*arg*/, esp_event_base_t /*base*/, int32_t /*event_id*/, void * /*event_data*/) { + if (eth_hostname[0] != '\0' && ETH.netif() != nullptr) { + esp_netif_set_hostname(ETH.netif(), eth_hostname); + } +} +#endif + // Ethernet management // Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver // continues to run autonomously (link negotiation, DHCP, etc) @@ -577,16 +593,44 @@ void Network::startEthernet() { eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720 : (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110 : ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110) + + // must be in place before ETH.begin() so it beats the esp_netif glue to the START event + strlcpy(eth_hostname, hostname_.c_str(), sizeof(eth_hostname)); + if (!eth_hostname_handler_registered_ && esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_START, ethApplyHostname, nullptr) == ESP_OK) { + eth_hostname_handler_registered_ = true; + } + if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) { - LOG_DEBUG("Ethernet module found - starting"); + LOG_DEBUG("Ethernet found - starting"); ethernet_connect_pending_ = true; - ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created + + // check whether DHCP already announced us under lwIP's default hostname, before we correct it + esp_netif_t * eth_netif = ETH.netif(); + const char * old_hostname = nullptr; + bool stale_dhcp = false; + if (eth_netif != nullptr && !staticIPConfig_) { + esp_netif_dhcp_status_t dhcp_status = ESP_NETIF_DHCP_INIT; + stale_dhcp = esp_netif_get_hostname(eth_netif, &old_hostname) == ESP_OK && old_hostname != nullptr && hostname_ != old_hostname + && esp_netif_dhcpc_get_status(eth_netif, &dhcp_status) == ESP_OK && dhcp_status == ESP_NETIF_DHCP_STARTED; + } + + if (!ETH.setHostname(hostname_.c_str())) { + LOG_WARNING("Failed to set Ethernet hostname to %s", hostname_.c_str()); + } + + // the DHCP server has already been given the wrong name, so re-run the client to re-announce + if (stale_dhcp) { + LOG_DEBUG("Restarting Ethernet DHCP client to announce hostname %s", hostname_.c_str()); + esp_netif_dhcpc_stop(eth_netif); + esp_netif_dhcpc_start(eth_netif); + } + ETH.enableIPv6(true); if (staticIPConfig_) { ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); } } else { - LOG_ERROR("Failed to start Ethernet module"); + LOG_ERROR("Failed to start Ethernet"); } #endif #endif diff --git a/src/core/network.h b/src/core/network.h index d081f0232..b9d1cc4ce 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -205,9 +205,10 @@ class Network { NetPhase phase_ = NetPhase::ETHERNET; - bool wifi_events_registered_ = false; // ensure WiFi.onEvent() handlers are registered only once across begin()/reconnect() cycles - bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP - bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP + bool wifi_events_registered_ = false; // ensure WiFi.onEvent() handlers are registered only once across begin()/reconnect() cycles + bool eth_hostname_handler_registered_ = false; // for the ETHERNET_EVENT_START hostname handler + bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP + bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP // Network and AP settings bool enableMDNS_;