Linux bluetooth development
 help / color / mirror / Atom feed
From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v1 04/10] adapter: Don't return Busy when a pending change sets the same value
Date: Fri, 30 Oct 2026 17:29:33 -0400	[thread overview]
Message-ID: <20261030212940.753771-5-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20261030212940.753771-1-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Registering an agent sets the adapter bondable, so setting Pairable
right after, e.g. with "pairable on" in bluetoothctl as soon as it
registered its default agent, fails with Busy although the pending
change already does what was requested.

Track the value of pending changes, and when a request sets the same
value, reply to it once the pending change completed with its result,
without sending the command again. Opposing requests still fail with
Busy.

Fixes: 348feb005a12 ("adapter: Return Busy error when setting is pending")
Assisted-by: Claude:claude-opus-5-5
---
 src/adapter.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 77 insertions(+), 4 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index edd2fdaf4db9..3c0895d1e241 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -280,6 +280,8 @@ struct btd_adapter {
 	char *short_name;		/* controller short name */
 	uint32_t supported_settings;	/* controller supported settings */
 	uint32_t pending_settings;	/* pending controller settings */
+	uint32_t pending_values;	/* values of pending settings */
+	struct queue *pending_props;	/* requests waiting a pending setting */
 	uint32_t power_state;		/* the power state */
 	uint32_t current_settings;	/* current controller settings */
 
@@ -745,6 +747,44 @@ struct set_mode_data {
 	uint8_t value;
 };
 
+struct pending_prop {
+	uint32_t setting;
+	GDBusPendingPropertySet id;
+	uint8_t status;
+};
+
+static bool reply_pending_prop(const void *data, const void *match_data)
+{
+	const struct pending_prop *prop = data;
+	const struct pending_prop *done = match_data;
+
+	if (!(prop->setting & done->setting))
+		return false;
+
+	if (done->status == MGMT_STATUS_SUCCESS)
+		g_dbus_pending_property_success(prop->id);
+	else if (done->status == MGMT_STATUS_RFKILLED)
+		g_dbus_pending_property_error(prop->id,
+					ERROR_INTERFACE ".Blocked",
+					mgmt_errstr(done->status));
+	else
+		g_dbus_pending_property_error(prop->id,
+					ERROR_INTERFACE ".Failed",
+					mgmt_errstr(done->status));
+
+	return true;
+}
+
+/* Reply to the requests that waited for a pending setting change */
+static void reply_pending_props(struct btd_adapter *adapter,
+					uint32_t setting, uint8_t status)
+{
+	struct pending_prop done = { .setting = setting, .status = status };
+
+	queue_remove_all(adapter->pending_props, reply_pending_prop, &done,
+									free);
+}
+
 static void set_mode_complete(uint8_t status, uint16_t length,
 					const void *param, void *user_data)
 {
@@ -761,6 +801,7 @@ static void set_mode_complete(uint8_t status, uint16_t length,
 		if (status != MGMT_STATUS_RFKILLED &&
 		    data->setting & MGMT_SETTING_POWERED)
 			reset_power_state_target(adapter, data->value);
+		reply_pending_props(adapter, data->setting, status);
 		return;
 	}
 
@@ -770,6 +811,7 @@ static void set_mode_complete(uint8_t status, uint16_t length,
 	 * event handling functions here.
 	 */
 	new_settings_callback(adapter->dev_id, length, param, adapter);
+	reply_pending_props(adapter, data->setting, status);
 }
 
 static void remove_temporary_devices(struct btd_adapter *adapter)
@@ -785,6 +827,17 @@ static void remove_temporary_devices(struct btd_adapter *adapter)
 	}
 }
 
+static void set_pending(struct btd_adapter *adapter, uint32_t setting,
+							uint8_t value)
+{
+	adapter->pending_settings |= setting;
+
+	if (value)
+		adapter->pending_values |= setting;
+	else
+		adapter->pending_values &= ~setting;
+}
+
 static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
 							uint8_t mode)
 {
@@ -828,7 +881,7 @@ static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
 	if (mgmt_send(adapter->mgmt, opcode,
 				adapter->dev_id, sizeof(cp), &cp,
 				set_mode_complete, data, g_free) > 0) {
-		adapter->pending_settings |= setting;
+		set_pending(adapter, setting, mode);
 		return true;
 	}
 	g_free(data);
@@ -3149,6 +3202,7 @@ static void property_set_mode_complete(uint8_t status, uint16_t length,
 		if (status != MGMT_STATUS_RFKILLED &&
 		    data->setting & MGMT_SETTING_POWERED)
 			reset_power_state_target(adapter, data->value);
+		reply_pending_props(adapter, data->setting, status);
 		return;
 	}
 
@@ -3160,6 +3214,7 @@ static void property_set_mode_complete(uint8_t status, uint16_t length,
 	 * event handling functions here.
 	 */
 	new_settings_callback(adapter->dev_id, length, param, adapter);
+	reply_pending_props(adapter, data->setting, status);
 }
 
 static void clear_discoverable(struct btd_adapter *adapter)
@@ -3199,9 +3254,23 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
 
 	dbus_message_iter_get_basic(value, &enable);
 
+	/* A pending change, e.g. bondable set when an agent registers,
+	 * already does what is requested if it sets the same value: reply
+	 * once it completed instead of sending the command again.
+	 */
 	if (adapter->pending_settings & setting) {
-		g_dbus_pending_property_error(id, ERROR_INTERFACE ".Busy",
-						NULL);
+		struct pending_prop *prop;
+
+		if (!!(adapter->pending_values & setting) != !!enable) {
+			g_dbus_pending_property_error(id,
+						ERROR_INTERFACE ".Busy", NULL);
+			return;
+		}
+
+		prop = new0(struct pending_prop, 1);
+		prop->setting = setting;
+		prop->id = id;
+		queue_push_tail(adapter->pending_props, prop);
 		return;
 	}
 
@@ -3289,7 +3358,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
 
 	if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
 			property_set_mode_complete, data, g_free) > 0) {
-		adapter->pending_settings |= setting;
+		set_pending(adapter, setting, mode);
 		return;
 	}
 
@@ -6244,6 +6313,9 @@ static void adapter_free(gpointer user_data)
 
 	queue_destroy(adapter->exp_pending, cancel_exp_pending);
 
+	reply_pending_props(adapter, UINT32_MAX, MGMT_STATUS_FAILED);
+	queue_destroy(adapter->pending_props, NULL);
+
 	/*
 	 * Unregister all handlers for this specific index since
 	 * the adapter bound to them is no longer valid.
@@ -7311,6 +7383,7 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
 	adapter->auths = g_queue_new();
 	adapter->exps = queue_new();
 	adapter->exp_pending = queue_new();
+	adapter->pending_props = queue_new();
 
 	return btd_adapter_ref(adapter);
 }
-- 
2.56.0


  parent reply	other threads:[~2026-10-30 21:29 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 21:29 [PATCH BlueZ v1 00/10] Run test-functional under User Mode Linux Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 01/10] tools/test-runner: Default to time travel with " Luiz Augusto von Dentz
2026-10-30 23:58   ` Run test-functional under " bluez.test.bot
2026-10-30 21:29 ` [PATCH BlueZ v1 02/10] tools/test-runner: Unbind virtio devices before powering off UML Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 03/10] emulator: Answer the time of a guest with a skew Luiz Augusto von Dentz
2026-10-30 21:29 ` Luiz Augusto von Dentz [this message]
2026-10-30 21:34   ` [PATCH BlueZ v1 04/10] adapter: Don't return Busy when a pending change sets the same value Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 05/10] test: functional: wait for BAP transports to be configured Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 06/10] shared/mcp: Don't drop commands before supported opcodes are read Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 07/10] doc/test-functional: Document User Mode Linux VM hosts Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 08/10] a2dp: Fix refusing the AVDTP connection of a new link Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 09/10] adapter: Don't cancel requests of a new link on stale disconnection Luiz Augusto von Dentz
2026-10-30 21:29 ` [PATCH BlueZ v1 10/10] test: Cover A2DP reconnection with a pending disconnection Luiz Augusto von Dentz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261030212940.753771-5-luiz.dentz@gmail.com \
    --to=luiz.dentz@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox