Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH] mesh: Fix Command Disallowed on random address change in generic IO
@ 2026-09-30 14:21 Aurélien Rivet
  2026-09-30 20:20 ` bluez.test.bot
  0 siblings, 1 reply; 4+ messages in thread
From: Aurélien Rivet @ 2026-09-30 14:21 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Luiz Augusto von Dentz, Aurélien Rivet

send_cancel_done() issues LE Set Random Address at the end of every
burst of ADVs. The controller rejects that command with Command
Disallowed (0x0c) while scanning is enabled, per Core Spec, Vol 4,
Part E, Section 7.8.4, so the address never rotates as long as any
scan is registered, such as a provisioner discovering unprovisioned
devices.

Pause scanning around the address change and resume it afterwards,
mirroring the disable/re-enable sequence restart_scan() already uses.
When no scan is registered there is nothing to pause, so the address
change is issued directly.

Confirmed with btmon on a Raspberry Pi built-in controller: over a
60 s capture, 12 rotations about 5 s apart, each one LE Set Scan
Enable(disable) -> LE Set Random Address -> LE Set Scan
Enable(enable), every command completing with Success and every
rotation programming a distinct address. No Command Disallowed
anywhere in the capture, where the address change previously failed
on every attempt. Scanning is suspended for 1.2 ms to 2.6 ms per
rotation, which mesh retransmissions absorb.

Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-opus-5
---

Resend: the original submission was archived by the CI bot after two
weeks without review, per the automated notice on the thread. The
patch is unchanged, rebased onto current master.

Original thread:
https://lore.kernel.org/linux-bluetooth/PAXP250MB0544B307EB9802855B711F71CBBB2@PAXP250MB0544.EURP250.PROD.OUTLOOK.COM/
 mesh/mesh-io-generic.c | 68 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 62 insertions(+), 6 deletions(-)

diff --git a/mesh/mesh-io-generic.c b/mesh/mesh-io-generic.c
index df1450ebe..7f65ec4f2 100644
--- a/mesh/mesh-io-generic.c
+++ b/mesh/mesh-io-generic.c
@@ -39,6 +39,7 @@ struct mesh_io_private {
 	struct tx_pkt *tx;
 	uint16_t interval;
 	bool sending;
+	bool scan_paused;
 	bool active;
 };
 
@@ -454,22 +455,77 @@ static bool dev_caps(struct mesh_io *io, struct mesh_io_caps *caps)
 	return true;
 }
 
-static void send_cancel_done(const void *buf, uint8_t size,
+static void set_random_address_rsp(const void *buf, uint8_t size,
 							void *user_data)
 {
 	struct mesh_io_private *pvt = user_data;
-	struct bt_hci_cmd_le_set_random_address cmd;
+	uint8_t status = *((uint8_t *) buf);
 
-	if (!pvt)
+	if (status)
+		l_error("LE Set Random Address failed (0x%02x)", status);
+
+	if (!pvt->scan_paused)
 		return;
 
-	pvt->sending = false;
+	pvt->scan_paused = false;
+
+	/* Only resume if a scan is still wanted: recv_deregister() may have
+	 * stopped it while the address change was in flight.
+	 */
+	if (!l_queue_isempty(pvt->io->rx_regs))
+		set_recv_scan_enable(NULL, 0, pvt);
+}
+
+static void send_set_random_address(struct mesh_io_private *pvt)
+{
+	struct bt_hci_cmd_le_set_random_address cmd;
 
-	/* At end of any burst of ADVs, change random address */
 	l_getrandom(cmd.addr, 6);
 	cmd.addr[5] |= 0xc0;
 	bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
-				&cmd, sizeof(cmd), NULL, NULL, NULL);
+				&cmd, sizeof(cmd), set_random_address_rsp,
+				pvt, NULL);
+}
+
+static void scan_disable_for_addr_change_rsp(const void *buf, uint8_t size,
+							void *user_data)
+{
+	struct mesh_io_private *pvt = user_data;
+	uint8_t status = *((uint8_t *) buf);
+
+	if (status)
+		l_error("LE Scan disable failed (0x%02x)", status);
+
+	send_set_random_address(pvt);
+}
+
+static void send_cancel_done(const void *buf, uint8_t size,
+							void *user_data)
+{
+	struct mesh_io_private *pvt = user_data;
+	struct bt_hci_cmd_le_set_scan_enable cmd;
+
+	if (!pvt)
+		return;
+
+	pvt->sending = false;
+
+	/* At end of any burst of ADVs, change random address. The controller
+	 * rejects LE Set Random Address with Command Disallowed while
+	 * scanning is enabled (Core Spec, Vol 4, Part E, Section 7.8.4).
+	 */
+	pvt->scan_paused = !l_queue_isempty(pvt->io->rx_regs);
+
+	if (!pvt->scan_paused) {
+		send_set_random_address(pvt);
+		return;
+	}
+
+	cmd.enable = 0x00;	/* Disable scanning */
+	cmd.filter_dup = 0x00;	/* Report duplicates */
+	bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+				&cmd, sizeof(cmd),
+				scan_disable_for_addr_change_rsp, pvt, NULL);
 }
 
 static void send_cancel(struct mesh_io_private *pvt)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread
* [PATCH] mesh: Fix Command Disallowed on random address change in generic IO
@ 2026-09-14  9:15 Aurélien Rivet
  2026-09-14 14:35 ` bluez.test.bot
  2026-09-29 18:55 ` bluez.test.bot
  0 siblings, 2 replies; 4+ messages in thread
From: Aurélien Rivet @ 2026-09-14  9:15 UTC (permalink / raw)
  To: linux-bluetooth

send_cancel_done() issues LE Set Random Address at the end of every
burst of ADVs. The controller rejects that command with Command
Disallowed (0x0c) while scanning is enabled, per Core Spec, Vol 4,
Part E, Section 7.8.4, so the address never rotates as long as any
scan is registered, such as a provisioner discovering unprovisioned
devices.

Pause scanning around the address change and resume it afterwards,
mirroring the disable/re-enable sequence restart_scan() already uses.
When no scan is registered there is nothing to pause, so the address
change is issued directly.

Confirmed with btmon on a Raspberry Pi built-in controller: over a
60 s capture, 12 rotations about 5 s apart, each one LE Set Scan
Enable(disable) -> LE Set Random Address -> LE Set Scan
Enable(enable), every command completing with Success and every
rotation programming a distinct address. No Command Disallowed
anywhere in the capture, where the address change previously failed
on every attempt. Scanning is suspended for 1.2 ms to 2.6 ms per
rotation, which mesh retransmissions absorb.

Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-opus-5
---
 mesh/mesh-io-generic.c | 68 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 62 insertions(+), 6 deletions(-)

diff --git a/mesh/mesh-io-generic.c b/mesh/mesh-io-generic.c
index df1450ebe..7f65ec4f2 100644
--- a/mesh/mesh-io-generic.c
+++ b/mesh/mesh-io-generic.c
@@ -39,6 +39,7 @@ struct mesh_io_private {
 	struct tx_pkt *tx;
 	uint16_t interval;
 	bool sending;
+	bool scan_paused;
 	bool active;
 };
 
@@ -454,22 +455,77 @@ static bool dev_caps(struct mesh_io *io, struct mesh_io_caps *caps)
 	return true;
 }
 
-static void send_cancel_done(const void *buf, uint8_t size,
+static void set_random_address_rsp(const void *buf, uint8_t size,
 							void *user_data)
 {
 	struct mesh_io_private *pvt = user_data;
-	struct bt_hci_cmd_le_set_random_address cmd;
+	uint8_t status = *((uint8_t *) buf);
 
-	if (!pvt)
+	if (status)
+		l_error("LE Set Random Address failed (0x%02x)", status);
+
+	if (!pvt->scan_paused)
 		return;
 
-	pvt->sending = false;
+	pvt->scan_paused = false;
+
+	/* Only resume if a scan is still wanted: recv_deregister() may have
+	 * stopped it while the address change was in flight.
+	 */
+	if (!l_queue_isempty(pvt->io->rx_regs))
+		set_recv_scan_enable(NULL, 0, pvt);
+}
+
+static void send_set_random_address(struct mesh_io_private *pvt)
+{
+	struct bt_hci_cmd_le_set_random_address cmd;
 
-	/* At end of any burst of ADVs, change random address */
 	l_getrandom(cmd.addr, 6);
 	cmd.addr[5] |= 0xc0;
 	bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
-				&cmd, sizeof(cmd), NULL, NULL, NULL);
+				&cmd, sizeof(cmd), set_random_address_rsp,
+				pvt, NULL);
+}
+
+static void scan_disable_for_addr_change_rsp(const void *buf, uint8_t size,
+							void *user_data)
+{
+	struct mesh_io_private *pvt = user_data;
+	uint8_t status = *((uint8_t *) buf);
+
+	if (status)
+		l_error("LE Scan disable failed (0x%02x)", status);
+
+	send_set_random_address(pvt);
+}
+
+static void send_cancel_done(const void *buf, uint8_t size,
+							void *user_data)
+{
+	struct mesh_io_private *pvt = user_data;
+	struct bt_hci_cmd_le_set_scan_enable cmd;
+
+	if (!pvt)
+		return;
+
+	pvt->sending = false;
+
+	/* At end of any burst of ADVs, change random address. The controller
+	 * rejects LE Set Random Address with Command Disallowed while
+	 * scanning is enabled (Core Spec, Vol 4, Part E, Section 7.8.4).
+	 */
+	pvt->scan_paused = !l_queue_isempty(pvt->io->rx_regs);
+
+	if (!pvt->scan_paused) {
+		send_set_random_address(pvt);
+		return;
+	}
+
+	cmd.enable = 0x00;	/* Disable scanning */
+	cmd.filter_dup = 0x00;	/* Report duplicates */
+	bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+				&cmd, sizeof(cmd),
+				scan_disable_for_addr_change_rsp, pvt, NULL);
 }
 
 static void send_cancel(struct mesh_io_private *pvt)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-30 20:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-30 14:21 [PATCH] mesh: Fix Command Disallowed on random address change in generic IO Aurélien Rivet
2026-09-30 20:20 ` bluez.test.bot
  -- strict thread matches above, loose matches on Subject: below --
2026-09-14  9:15 [PATCH] " Aurélien Rivet
2026-09-14 14:35 ` bluez.test.bot
2026-09-29 18:55 ` bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox