[bluetooth_connection] Keep the stack-down reset in one place

The loop backstop only ran one pass earlier than the tracker hook and forced
the reset into a helper with extra branches; the hook alone now does the
reset. The unregistered-interface check moves to connect(), where it covers
both the INIT state and the window before REG_EVT with one predicate.
This commit is contained in:
J. Nick Koston
2026-09-10 07:51:57 -05:00
parent 8c772b4dd4
commit 68cbe019c6
5 changed files with 14 additions and 38 deletions
@@ -45,9 +45,7 @@ void BluedroidGattClient::setup() {
void BluedroidGattClient::loop() {
if (!esp32_ble::global_ble->is_active()) {
// Backstop for the window between disable() and the teardown; the
// tracker's before-disabled hook is the primary path.
this->reset_for_stack_down_();
// ble_before_disabled_event_handler() settles the slot.
return;
}
auto st = this->state();
@@ -82,25 +80,17 @@ void BluedroidGattClient::loop() {
}
// Stack down: no CLOSE_EVT will come. Settle a live link so the consumer
// frees its slot, then re-register the app on the next enable.
void BluedroidGattClient::reset_for_stack_down_() {
// frees its slot, then register the app again on the next enable.
void BluedroidGattClient::ble_before_disabled_event_handler() {
auto st = this->state();
if (st == ClientState::INIT) {
return;
}
if (st != ClientState::IDLE) {
if (st != ClientState::IDLE && st != ClientState::INIT) {
this->release_services();
this->set_idle_();
this->listener_->on_connection_state(false, 0, ble_device_base::GATT_ERR_NOT_CONNECTED);
}
// The interface belongs to the torn-down stack; Bluedroid drops an open
// issued with it without any event.
// The interface belongs to the torn-down stack.
this->gattc_if_ = ESP_GATT_IF_NONE;
this->set_state(ClientState::INIT);
}
void BluedroidGattClient::ble_before_disabled_event_handler() {
this->reset_for_stack_down_();
// An idle slot runs no loop; the INIT branch must run to register again.
this->enable_loop();
}
@@ -115,15 +105,14 @@ void BluedroidGattClient::dump_config() {
// ---- contract ops ----
int BluedroidGattClient::connect(uint64_t address, uint8_t addr_type) {
auto st = this->state();
if (st == ClientState::INIT) {
// Nothing can be opened until the app is registered on a running stack.
ESP_LOGW(TAG, "[%d] Connect rejected, BLE stack not ready", this->connection_index_);
if (this->gattc_if_ == ESP_GATT_IF_NONE) {
// Bluedroid drops an open on an unknown interface without any event.
ESP_LOGW(TAG, "[%d] Connect rejected, GATT app not registered", this->connection_index_);
return ble_device_base::GATT_ERR_NOT_CONNECTED;
}
// Only from idle: clobbering DISCONNECTING would open a new link the
// stale CLOSE_EVT then tears down.
if (st != ClientState::IDLE) {
if (this->state() != ClientState::IDLE) {
ESP_LOGW(TAG, "[%d] Connect rejected, slot busy", this->connection_index_);
return ESP_GATT_BUSY;
}
@@ -145,14 +134,6 @@ void BluedroidGattClient::tracker_connect_() {
ESP_LOGW(TAG, "[%d] Cannot connect, still waiting for CLOSE_EVT", this->connection_index_);
return;
}
if (this->gattc_if_ == ESP_GATT_IF_NONE) {
// REG_EVT has not landed on this stack; Bluedroid drops an open on an
// unknown interface without any event, which would wedge the slot.
ESP_LOGW(TAG, "[%d] Connect failed, GATT app not registered", this->connection_index_);
this->set_idle_();
this->listener_->on_connection_state(false, 0, ble_device_base::GATT_ERR_NOT_CONNECTED);
return;
}
ESP_LOGI(TAG, "[%d] 0x%02x Connecting", this->connection_index_, this->remote_addr_type_);
// Per-attempt latches; the search machine is reset by set_idle_(), the
// one door back to IDLE.
@@ -99,7 +99,6 @@ class BluedroidGattClient final : public esp32_ble_tracker::ESPBTClient, public
void unconditional_disconnect_();
void set_idle_();
void set_disconnecting_();
void reset_for_stack_down_();
esp_err_t update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout,
const char *param_type);
int check_and_log_error_(const char *operation, esp_err_t err);
+1 -3
View File
@@ -580,9 +580,7 @@ void ESP32BLE::loop_handle_state_transition_not_active_() {
this->mark_failed();
return;
}
// Whatever the old stack queued while going down (close events for the
// links it tore down, scan completions) must not replay against the next
// stack, which hands out the same interface ids.
// Drop what the old stack queued; the next stack reuses the same interface ids.
BLEEvent *ble_event;
while ((ble_event = this->ble_events_.pop()) != nullptr) {
this->ble_event_pool_.release(ble_event);
@@ -42,7 +42,7 @@ void BLEClientBase::set_state(espbt::ClientState st) {
void BLEClientBase::loop() {
if (!esp32_ble::global_ble->is_active()) {
this->set_state(espbt::ClientState::INIT);
// ble_before_disabled_event_handler() resets the client.
return;
}
if (this->state() == espbt::ClientState::INIT) {
@@ -73,9 +73,8 @@ void BLEClientBase::loop() {
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void BLEClientBase::ble_before_disabled_event_handler() {
// Same reset as the stack-down branch of loop(); an idle client has its
// loop disabled, so enable it for the INIT registration on the next enable.
this->set_state(espbt::ClientState::INIT);
// An idle client runs no loop; the INIT branch must run to register again.
this->enable_loop();
}
@@ -225,9 +225,8 @@ void ESP32BLETracker::ble_before_disabled_event_handler() {
client->ble_before_disabled_event_handler();
}
#endif
// The stop above never completes: the stack is torn down and its queued
// events are dropped, so settle the scanner here. start_scan_() refuses
// anything but IDLE once the stack is back.
// The stop above never completes (stack torn down, events dropped); settle
// here so start_scan_() sees IDLE once the stack is back.
if (this->scanner_state_ != ScannerState::IDLE) {
#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
this->skip_next_scan_end_ = false;