mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
Decide the timeout escalation under the lock and drop links at OTA start
This commit is contained in:
@@ -104,9 +104,24 @@ void RP2GattClient::setup() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
ota::get_global_ota_callback()->add_global_state_listener(this);
|
||||
#endif
|
||||
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void RP2GattClient::on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
||||
// esp32 parity (its tracker disconnects every client at OTA start): free
|
||||
// the shared radio for the transfer. No restore needed; the client
|
||||
// reconnects, and on success the device reboots anyway.
|
||||
if (state == ota::OTA_STARTED && this->state_ != EngineState::IDLE) {
|
||||
this->gatt_disconnect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
float RP2GattClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
|
||||
|
||||
void RP2GattClient::dump_config() { ESP_LOGCONFIG(TAG, "RP2 GATT client (BTstack)"); }
|
||||
@@ -446,39 +461,41 @@ void RP2GattClient::loop() {
|
||||
uint32_t budget = cancel_in_flight ? CONNECT_CANCEL_TIMEOUT_MS : CONNECT_TIMEOUT_MS;
|
||||
if (now - this->connect_started_ > budget) {
|
||||
ESP_LOGW(TAG, "[%u] Connect timeout", this->engine_index_);
|
||||
if (this->state_ == EngineState::CONNECTING && this->con_handle_ == HCI_CON_HANDLE_INVALID) {
|
||||
if (!this->connect_cancel_attempted_) {
|
||||
this->connect_cancel_attempted_ = true;
|
||||
BluetoothLock lock;
|
||||
bool link_up = this->state_ != EngineState::CONNECTING;
|
||||
bool cancel_sent = false;
|
||||
if (!link_up) {
|
||||
BluetoothLock lock;
|
||||
// Handle check under the lock: a success completion can stamp it in
|
||||
// the BTstack context right up to this point, and escalating past a
|
||||
// live link would orphan it (the queued CONNECTED event is dropped
|
||||
// by the state guard once fail_connection_ runs).
|
||||
link_up = this->con_handle_ != HCI_CON_HANDLE_INVALID;
|
||||
if (!link_up && connect_owner == this) {
|
||||
// gap_connect_cancel is stack-global; only the engine whose
|
||||
// create-connection is in flight may issue it.
|
||||
if (connect_owner == this) {
|
||||
gap_connect_cancel();
|
||||
// The cancel produces a connection-complete event with a failure
|
||||
// status, which drives the normal failure path; restart the timer
|
||||
// so a lost event escalates on the short cancel budget instead of
|
||||
// wedging here.
|
||||
this->connect_started_ = now;
|
||||
return;
|
||||
}
|
||||
// create-connection is in flight may issue it. First timeout:
|
||||
// cancel and give the completion a grace period. Second: the
|
||||
// completion was lost, re-issue the cancel in case the procedure
|
||||
// still runs (a no-op on an idle stack), then escalate.
|
||||
gap_connect_cancel();
|
||||
cancel_sent = !this->connect_cancel_attempted_;
|
||||
}
|
||||
{
|
||||
// The cancel's completion never arrived: re-issue it in case the
|
||||
// procedure still runs (a no-op on an idle stack), then reclaim the
|
||||
// slot and the scan rather than cancelling forever.
|
||||
BluetoothLock lock;
|
||||
if (connect_owner == this) {
|
||||
gap_connect_cancel();
|
||||
}
|
||||
}
|
||||
this->fail_connection_(HCI_REASON_CONNECTION_TIMEOUT);
|
||||
} else {
|
||||
// The link is up (MTU exchange stalled): tear it down properly so the
|
||||
// controller frees its side; the DISCONNECTING safety net below
|
||||
// reclaims state if the disconnection event is lost. Dropping engine
|
||||
// state without gap_disconnect would leak the live link and this
|
||||
// engine's GATT slot for the rest of the boot.
|
||||
this->connect_cancel_attempted_ = true;
|
||||
}
|
||||
if (link_up) {
|
||||
// The link is up (stamped mid-timeout or MTU exchange stalled): tear
|
||||
// it down properly so the controller frees its side; the
|
||||
// DISCONNECTING safety net below reclaims state if the disconnection
|
||||
// event is lost. Dropping engine state without gap_disconnect would
|
||||
// leak the live link and this engine's GATT slot for the rest of the
|
||||
// boot.
|
||||
this->gatt_disconnect();
|
||||
} else if (cancel_sent) {
|
||||
// The cancel produces a connection-complete event with a failure
|
||||
// status, which drives the normal failure path; restart the timer so
|
||||
// a lost event escalates on the short cancel budget.
|
||||
this->connect_started_ = now;
|
||||
} else {
|
||||
this->fail_connection_(HCI_REASON_CONNECTION_TIMEOUT);
|
||||
}
|
||||
}
|
||||
} else if (this->state_ == EngineState::DISCONNECTING) {
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/lock_free_queue.h"
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#endif
|
||||
|
||||
#include <btstack.h>
|
||||
|
||||
#include <array>
|
||||
@@ -71,7 +75,13 @@ static constexpr uint8_t RP2_GATT_EVENT_QUEUE_SIZE = 8;
|
||||
// full 512 B ATT payload, so depth buys burst tolerance at ~516 B per slot.
|
||||
static constexpr uint8_t RP2_GATT_NOTIFY_QUEUE_SIZE = 4;
|
||||
|
||||
class RP2GattClient final : public Component, public Parented<rp2040_ble::RP2040BLE> {
|
||||
class RP2GattClient final : public Component,
|
||||
public Parented<rp2040_ble::RP2040BLE>
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
,
|
||||
public ota::OTAGlobalStateListener
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
@@ -100,6 +110,12 @@ class RP2GattClient final : public Component, public Parented<rp2040_ble::RP2040
|
||||
void set_connection_type(ble_device_base::ConnectionType ct) { this->connection_type_ = ct; }
|
||||
void release_services();
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
// Drop the connection while an OTA runs (esp32 parity): an active link
|
||||
// competes with the transfer for the shared radio.
|
||||
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Link/engine state. Discovery and GATT ops have their own cursors below —
|
||||
// the link stays READY while they run.
|
||||
|
||||
Reference in New Issue
Block a user