mirror of
https://github.com/esphome/esphome.git
synced 2025-09-17 10:42:21 +01:00
[ethernet] Fix permanent component failure from undocumented ESP_FAIL in IPv6 setup (#10708)
This commit is contained in:
committed by
Jesse Hills
parent
5b5e5c213c
commit
646f4e66be
@@ -300,6 +300,7 @@ void EthernetComponent::loop() {
|
|||||||
this->state_ = EthernetComponentState::CONNECTING;
|
this->state_ = EthernetComponentState::CONNECTING;
|
||||||
this->start_connect_();
|
this->start_connect_();
|
||||||
} else {
|
} else {
|
||||||
|
this->finish_connect_();
|
||||||
// When connected and stable, disable the loop to save CPU cycles
|
// When connected and stable, disable the loop to save CPU cycles
|
||||||
this->disable_loop();
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
@@ -486,10 +487,35 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_
|
|||||||
}
|
}
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
|
|
||||||
|
void EthernetComponent::finish_connect_() {
|
||||||
|
#if USE_NETWORK_IPV6
|
||||||
|
// Retry IPv6 link-local setup if it failed during initial connect
|
||||||
|
// This handles the case where min_ipv6_addr_count is NOT set (or is 0),
|
||||||
|
// allowing us to reach CONNECTED state with just IPv4.
|
||||||
|
// If IPv6 setup failed in start_connect_() because the interface wasn't ready:
|
||||||
|
// - Bootup timing issues (#10281)
|
||||||
|
// - Cable unplugged/network interruption (#10705)
|
||||||
|
// We can now retry since we're in CONNECTED state and the interface is definitely up.
|
||||||
|
if (!this->ipv6_setup_done_) {
|
||||||
|
esp_err_t err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "IPv6 link-local address created (retry succeeded)");
|
||||||
|
}
|
||||||
|
// Always set the flag to prevent continuous retries
|
||||||
|
// If IPv6 setup fails here with the interface up and stable, it's
|
||||||
|
// likely a persistent issue (IPv6 disabled at router, hardware
|
||||||
|
// limitation, etc.) that won't be resolved by further retries.
|
||||||
|
// The device continues to work with IPv4.
|
||||||
|
this->ipv6_setup_done_ = true;
|
||||||
|
}
|
||||||
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
|
}
|
||||||
|
|
||||||
void EthernetComponent::start_connect_() {
|
void EthernetComponent::start_connect_() {
|
||||||
global_eth_component->got_ipv4_address_ = false;
|
global_eth_component->got_ipv4_address_ = false;
|
||||||
#if USE_NETWORK_IPV6
|
#if USE_NETWORK_IPV6
|
||||||
global_eth_component->ipv6_count_ = 0;
|
global_eth_component->ipv6_count_ = 0;
|
||||||
|
this->ipv6_setup_done_ = false;
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
this->connect_begin_ = millis();
|
this->connect_begin_ = millis();
|
||||||
this->status_set_warning(LOG_STR("waiting for IP configuration"));
|
this->status_set_warning(LOG_STR("waiting for IP configuration"));
|
||||||
@@ -545,9 +571,27 @@ void EthernetComponent::start_connect_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if USE_NETWORK_IPV6
|
#if USE_NETWORK_IPV6
|
||||||
|
// Attempt to create IPv6 link-local address
|
||||||
|
// We MUST attempt this here, not just in finish_connect_(), because with
|
||||||
|
// min_ipv6_addr_count set, the component won't reach CONNECTED state without IPv6.
|
||||||
|
// However, this may fail with ESP_FAIL if the interface is not up yet:
|
||||||
|
// - At bootup when link isn't ready (#10281)
|
||||||
|
// - After disconnection/cable unplugged (#10705)
|
||||||
|
// We'll retry in finish_connect_() if it fails here.
|
||||||
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed");
|
if (err == ESP_ERR_ESP_NETIF_INVALID_PARAMS) {
|
||||||
|
// This is a programming error, not a transient failure
|
||||||
|
ESPHL_ERROR_CHECK(err, "esp_netif_create_ip6_linklocal invalid parameters");
|
||||||
|
} else {
|
||||||
|
// ESP_FAIL means the interface isn't up yet
|
||||||
|
// This is expected and non-fatal, happens in multiple scenarios:
|
||||||
|
// - During reconnection after network interruptions (#10705)
|
||||||
|
// - At bootup when the link isn't ready yet (#10281)
|
||||||
|
// We'll retry once we reach CONNECTED state and the interface is up
|
||||||
|
ESP_LOGW(TAG, "esp_netif_create_ip6_linklocal failed: %s", esp_err_to_name(err));
|
||||||
|
// Don't mark component as failed - this is a transient error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
|
|
||||||
|
@@ -102,6 +102,7 @@ class EthernetComponent : public Component {
|
|||||||
#endif /* LWIP_IPV6 */
|
#endif /* LWIP_IPV6 */
|
||||||
|
|
||||||
void start_connect_();
|
void start_connect_();
|
||||||
|
void finish_connect_();
|
||||||
void dump_connect_params_();
|
void dump_connect_params_();
|
||||||
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
|
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
|
||||||
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
|
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
|
||||||
@@ -144,6 +145,7 @@ class EthernetComponent : public Component {
|
|||||||
bool got_ipv4_address_{false};
|
bool got_ipv4_address_{false};
|
||||||
#if LWIP_IPV6
|
#if LWIP_IPV6
|
||||||
uint8_t ipv6_count_{0};
|
uint8_t ipv6_count_{0};
|
||||||
|
bool ipv6_setup_done_{false};
|
||||||
#endif /* LWIP_IPV6 */
|
#endif /* LWIP_IPV6 */
|
||||||
|
|
||||||
// Pointers at the end (naturally aligned)
|
// Pointers at the end (naturally aligned)
|
||||||
|
Reference in New Issue
Block a user