Linux bluetooth development
 help / color / mirror / Atom feed
* [BlueZ 5/6] client: Use _cleanup_fd_ to simplify urandom access
From: Bastien Nocera @ 2026-06-09 13:50 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260609135837.476561-1-hadess@hadess.net>

fd gets auto-closed before exiting the scope.
---
 client/mgmt.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/client/mgmt.c b/client/mgmt.c
index 50558a313866..e199c30540e4 100644
--- a/client/mgmt.c
+++ b/client/mgmt.c
@@ -2681,7 +2681,7 @@ static void cmd_privacy(int argc, char **argv)
 			return bt_shell_noninteractive_quit(EXIT_FAILURE);
 		}
 	} else {
-		int fd;
+		_cleanup_fd_ int fd = -1;
 
 		fd = open("/dev/urandom", O_RDONLY);
 		if (fd < 0) {
@@ -2691,11 +2691,8 @@ static void cmd_privacy(int argc, char **argv)
 
 		if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) {
 			error("Reading from urandom failed");
-			close(fd);
 			return bt_shell_noninteractive_quit(EXIT_FAILURE);
 		}
-
-		close(fd);
 	}
 
 	if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
-- 
2.54.0


^ permalink raw reply related

* [BlueZ 6/6] btattach: Use _cleanup_fd_ to simplify error paths
From: Bastien Nocera @ 2026-06-09 13:50 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260609135837.476561-1-hadess@hadess.net>

Use _cleanup_fd_ and _steal_fd() to simplify error paths, and only
"steal" the file descriptor on success.
---
 tools/btattach.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/tools/btattach.c b/tools/btattach.c
index 5f7d19093698..4918c79f65c7 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -40,7 +40,8 @@
 static int open_serial(const char *path, unsigned int speed, bool flowctl)
 {
 	struct termios ti;
-	int fd, saved_ldisc, ldisc = N_HCI;
+	_cleanup_fd_ int fd = -1;
+	int saved_ldisc, ldisc = N_HCI;
 
 	fd = open(path, O_RDWR | O_NOCTTY);
 	if (fd < 0) {
@@ -50,13 +51,11 @@ static int open_serial(const char *path, unsigned int speed, bool flowctl)
 
 	if (tcflush(fd, TCIOFLUSH) < 0) {
 		perror("Failed to flush serial port");
-		close(fd);
 		return -1;
 	}
 
 	if (ioctl(fd, TIOCGETD, &saved_ldisc) < 0) {
 		perror("Failed get serial line discipline");
-		close(fd);
 		return -1;
 	}
 
@@ -73,19 +72,17 @@ static int open_serial(const char *path, unsigned int speed, bool flowctl)
 
 	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
 		perror("Failed to set serial port settings");
-		close(fd);
 		return -1;
 	}
 
 	if (ioctl(fd, TIOCSETD, &ldisc) < 0) {
 		perror("Failed set serial line discipline");
-		close(fd);
 		return -1;
 	}
 
 	printf("Switched line discipline from %d to %d\n", saved_ldisc, ldisc);
 
-	return fd;
+	return _steal_fd_(fd);
 }
 
 static void local_version_callback(const void *data, uint8_t size,
@@ -99,7 +96,8 @@ static void local_version_callback(const void *data, uint8_t size,
 static int attach_proto(const char *path, unsigned int proto,
 			unsigned int speed, bool flowctl, unsigned int flags)
 {
-	int fd, dev_id;
+	_cleanup_fd_ int fd = -1;
+	int dev_id;
 
 	fd = open_serial(path, speed, flowctl);
 	if (fd < 0)
@@ -107,20 +105,17 @@ static int attach_proto(const char *path, unsigned int proto,
 
 	if (ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
 		perror("Failed to set flags");
-		close(fd);
 		return -1;
 	}
 
 	if (ioctl(fd, HCIUARTSETPROTO, proto) < 0) {
 		perror("Failed to set protocol");
-		close(fd);
 		return -1;
 	}
 
 	dev_id = ioctl(fd, HCIUARTGETDEVICE);
 	if (dev_id < 0) {
 		perror("Failed to get device id");
-		close(fd);
 		return -1;
 	}
 
@@ -140,7 +135,6 @@ static int attach_proto(const char *path, unsigned int proto,
 
 		if (!hci) {
 			fprintf(stderr, "Failed to open HCI user channel\n");
-			close(fd);
 			return -1;
 		}
 
@@ -149,7 +143,7 @@ static int attach_proto(const char *path, unsigned int proto,
 					(bt_hci_destroy_func_t) bt_hci_unref);
 	}
 
-	return fd;
+	return _steal_fd_(fd);
 }
 
 static void uart_callback(int fd, uint32_t events, void *user_data)
-- 
2.54.0


^ permalink raw reply related

* RE: Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: bluez.test.bot @ 2026-06-09 14:38 UTC (permalink / raw)
  To: linux-bluetooth, senozhatsky
In-Reply-To: <20260609121329.1262170-1-senozhatsky@chromium.org>

[-- Attachment #1: Type: text/plain, Size: 988 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1108559

---Test result---

Test Summary:
CheckPatch                    PASS      0.74 seconds
VerifyFixes                   PASS      0.13 seconds
VerifySignedoff               PASS      0.13 seconds
GitLint                       PASS      0.37 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      26.65 seconds
CheckAllWarning               PASS      29.25 seconds
CheckSparse                   PASS      27.91 seconds
BuildKernel32                 PASS      26.67 seconds
TestRunnerSetup               PASS      550.04 seconds
IncrementalBuild              PASS      25.31 seconds



https://github.com/bluez/bluetooth-next/pull/299

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [Bug 221627] New: MT7925E - System locks up with flashing caps lock
From: bugzilla-daemon @ 2026-06-09 15:08 UTC (permalink / raw)
  To: linux-bluetooth

https://bugzilla.kernel.org/show_bug.cgi?id=221627

            Bug ID: 221627
           Summary: MT7925E - System locks up with flashing caps lock
           Product: Drivers
           Version: 2.5
          Hardware: AMD
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P3
         Component: Bluetooth
          Assignee: linux-bluetooth@vger.kernel.org
          Reporter: tom@helix.me.uk
        Regression: No

Created attachment 310284
  --> https://bugzilla.kernel.org/attachment.cgi?id=310284&action=edit
kernel config and wifi/bluetooth journals

I'm not sure the circumstance that causes the crash, but it doesn't happen with
bluetooth soft blocked. I'm guessing it's during scanning. I have no devices
paired, but many bluetooth devices in the house.

However it could still be an issue with the wifi.

I have a MT7925 as my WiFi module with the MT7925E driver installed as a
module. No other WiFi cards are compiled into the kernel.

I'm running on a Framework Laptop 13 / AMD Ryzen 7 7840U on 7.1.0-rc7.

I can't find any logs related to the crashing, however when restarting (holding
power, then turning back on) Sometimes the TTY gets filled with errors and the
system locks up again. I haven’t had this happen for a while but will attach a
screenshot of these logs when it happens again.

I've attached my kernel config file and the logs I have.

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* Re: [BlueZ PATCH v3 0/2] shared/bap: add ASE Control Point error responses
From: patchwork-bot+bluetooth @ 2026-06-09 15:20 UTC (permalink / raw)
  To: raghu447; +Cc: linux-bluetooth
In-Reply-To: <20260609102548.6887-1-raghavendra.rao@collabora.com>

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Tue,  9 Jun 2026 15:55:46 +0530 you wrote:
> Added bap unit tests in a seperate patch.
> These changes are required to Pass BAP/USR/SPE/BI-01[5]-C tests.
> 
> raghu447 (2):
>   shared/bap: add ASE Control Point error responses
>   unit/bap: add SPE tests
> 
> [...]

Here is the summary with links:
  - [BlueZ,v3,1/2] shared/bap: add ASE Control Point error responses
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=8bc6dd81c9c2
  - [BlueZ,v3,2/2] unit/bap: add SPE tests
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=912d67d1d3a4

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH BlueZ v1 1/2] shared/hci: Avoid redundant BPF filter updates on duplicate events
From: patchwork-bot+bluetooth @ 2026-06-09 15:20 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <20260608205009.97585-1-luiz.dentz@gmail.com>

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Mon,  8 Jun 2026 16:50:08 -0400 you wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> Skip updating the BPF socket filter in bt_hci_register and
> bt_hci_register_subevent when the event/subevent is already
> registered, since it is already part of the filter.
> 
> Similarly, skip updating the filter in bt_hci_unregister and
> bt_hci_unregister_subevent when other handlers for the same
> event/subevent still remain in the queue.
> 
> [...]

Here is the summary with links:
  - [BlueZ,v1,1/2] shared/hci: Avoid redundant BPF filter updates on duplicate events
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=27eb493882ae
  - [BlueZ,v1,2/2] shared/hci: Debounce SO_ATTACH_FILTER with timeout_add(0)
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=9320e1cb4749

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* [PATCH v1] Bluetooth: btintel_pcie: Separate coredump work from RX work
From: Kiran K @ 2026-06-09 15:46 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
	chandrashekar.devegowda, ravindra, Kiran K

From: Ravindra <ravindra@intel.com>

Sharing a single workqueue between coredump processing and RX
delays evacuation of RX events while a coredump is in progress.
The firmware's RX buffers can overflow during that window, leading
to dropped events. The issue was observed in HID use cases where
HID reports arrive in bursts and quickly fill the RX path while a
coredump is being collected.

Move coredump processing to a dedicated ordered coredump_workqueue
with its own coredump_work, so coredumps run independently of RX.
All four coredump trigger sources (FW assert, HW exception, user
sysfs trigger, and resume-error detection) are switched to this new
workqueue. Ordering serialises concurrent triggers without blocking
RX.

Signed-off-by: Ravindra <ravindra@intel.com>
Signed-off-by: Kiran K <kiran.k@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 86 ++++++++++++++++++++++++++------
 drivers/bluetooth/btintel_pcie.h |  5 ++
 2 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 2baef130d101..fbc7b10cdfe3 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1426,7 +1426,7 @@ static void btintel_pcie_msix_fw_trigger_handler(struct btintel_pcie_data *data)
 	if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
 		data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
 
-	queue_work(data->workqueue, &data->rx_work);
+	queue_work(data->coredump_workqueue, &data->coredump_work);
 }
 
 static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
@@ -1443,16 +1443,21 @@ static void btintel_pcie_msix_hw_exp_handler(struct btintel_pcie_data *data)
 	if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
 		data->dmp_hdr.trigger_reason = BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
 
-	queue_work(data->workqueue, &data->rx_work);
+	queue_work(data->coredump_workqueue, &data->coredump_work);
 }
 
-static void btintel_pcie_rx_work(struct work_struct *work)
+static void btintel_pcie_coredump_worker(struct work_struct *work)
 {
 	struct btintel_pcie_data *data = container_of(work,
-					struct btintel_pcie_data, rx_work);
-	struct sk_buff *skb;
+					struct btintel_pcie_data, coredump_work);
 	int err;
 
+	/* hdev is NULL until setup_hdev() succeeds, and is cleared on
+	 * teardown after disable_work_sync() drains us; bail in that case.
+	 */
+	if (!data->hdev)
+		return;
+
 	if (test_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags)) {
 		err = btintel_pcie_dump_fwtrigger_event(data);
 		if (err)
@@ -1476,6 +1481,13 @@ static void btintel_pcie_rx_work(struct work_struct *work)
 		btintel_pcie_read_hwexp(data);
 		clear_bit(BTINTEL_PCIE_HWEXP_INPROGRESS, &data->flags);
 	}
+}
+
+static void btintel_pcie_rx_work(struct work_struct *work)
+{
+	struct btintel_pcie_data *data = container_of(work,
+					struct btintel_pcie_data, rx_work);
+	struct sk_buff *skb;
 
 	/* Process the sk_buf in queue and send to the HCI layer */
 	while ((skb = skb_dequeue(&data->rx_skb_q))) {
@@ -2572,6 +2584,10 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
 	btintel_pcie_synchronize_irqs(data);
 
 	flush_work(&data->rx_work);
+	/* Drain any in-flight coredump and block new ones across reset.
+	 * Safe from self-deadlock: coredump_work runs on a separate wq.
+	 */
+	disable_work_sync(&data->coredump_work);
 
 	bt_dev_dbg(data->hdev, "Release bluetooth interface");
 	if (data->reset_type == BTINTEL_PCIE_IOSF_PRR_PLDR) {
@@ -2579,6 +2595,11 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
 		 * pci_rescan_remove_lock. This mutex serializes against PCI device
 		 * addition/removal (hotplug), so no device can be added to or
 		 * removed from the bus list while this code runs.
+		 *
+		 * device_reprobe() inside btintel_pcie_perform_pldr() destroys
+		 * 'data' via .remove(); a fresh probe re-INIT_WORKs the
+		 * coredump_work with disable count 0, so we must not call
+		 * enable_work() on this path.
 		 */
 		btintel_pcie_perform_pldr(data);
 		goto out;
@@ -2588,7 +2609,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
 	err = pci_reset_function(pdev);
 	if (err) {
 		BT_ERR("Failed resetting the pcie device (%d)", err);
-		goto out;
+		goto out_enable;
 	}
 
 	btintel_pcie_enable_interrupts(data);
@@ -2598,7 +2619,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
 	if (err) {
 		BT_ERR("Failed to enable bluetooth hardware after reset (%d)",
 		       err);
-		goto out;
+		goto out_enable;
 	}
 
 	btintel_pcie_reset_ia(data);
@@ -2608,8 +2629,15 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
 	err = btintel_pcie_setup_hdev(data);
 	if (err) {
 		BT_ERR("Failed registering hdev (%d)", err);
-		goto out;
+		goto out_enable;
 	}
+
+out_enable:
+	/* Balance disable_work_sync() above on every exit. Leaving the
+	 * counter incremented on a failed reset would permanently disable
+	 * coredump_work even after a later successful reset.
+	 */
+	enable_work(&data->coredump_work);
 out:
 	pci_dev_put(pdev);
 	pci_unlock_rescan_remove();
@@ -2743,7 +2771,6 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
 	hdev->bus = HCI_PCI;
 	hci_set_drvdata(hdev, data);
 
-	data->hdev = hdev;
 	SET_HCIDEV_DEV(hdev, &data->pdev->dev);
 
 	hdev->manufacturer = 2;
@@ -2762,15 +2789,17 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
 	err = hci_register_dev(hdev);
 	if (err < 0) {
 		BT_ERR("Failed to register to hdev (%d)", err);
-		goto exit_error;
+		hci_free_dev(hdev);
+		return err;
 	}
 
+	/* Publish hdev only after successful registration; the coredump
+	 * worker bails on !data->hdev, so it never observes a half-set-up
+	 * device.
+	 */
+	data->hdev = hdev;
 	data->dmp_hdr.driver_name = KBUILD_MODNAME;
 	return 0;
-
-exit_error:
-	hci_free_dev(hdev);
-	return err;
 }
 
 static int btintel_pcie_probe(struct pci_dev *pdev,
@@ -2801,9 +2830,16 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
 	if (!data->workqueue)
 		return -ENOMEM;
 
+	data->coredump_workqueue = alloc_ordered_workqueue(KBUILD_MODNAME "_cd", 0);
+	if (!data->coredump_workqueue) {
+		destroy_workqueue(data->workqueue);
+		return -ENOMEM;
+	}
+
 	skb_queue_head_init(&data->rx_skb_q);
 	INIT_WORK(&data->rx_work, btintel_pcie_rx_work);
 	INIT_WORK(&data->reset_work, btintel_pcie_reset_work);
+	INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
 
 	data->boot_stage_cache = 0x00;
 	data->img_resp_cache = 0x00;
@@ -2846,6 +2882,8 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
 	/* reset device before exit */
 	btintel_pcie_reset_bt(data);
 
+	destroy_workqueue(data->coredump_workqueue);
+
 	pci_clear_master(pdev);
 
 	pci_set_drvdata(pdev, NULL);
@@ -2863,6 +2901,13 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
 		return;
 	}
 
+	/* Permanently block coredump triggers and drain the worker before
+	 * tearing down. Must run before cancel_work_sync(&reset_work) so
+	 * the disable counter stays >= 1 even after reset_work()'s
+	 * balanced enable_work() (counter 2 -> 1, never reaching 0).
+	 */
+	disable_work_sync(&data->coredump_work);
+
 	/* Cancel pending reset work. Skip only when remove() is called from
 	 * within the reset work itself (PLDR device_reprobe path) to avoid
 	 * deadlock. current_work() returns the work_struct of the caller if
@@ -2889,6 +2934,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
 
 	btintel_pcie_release_hdev(data);
 
+	destroy_workqueue(data->coredump_workqueue);
 	destroy_workqueue(data->workqueue);
 
 	btintel_pcie_free(data);
@@ -2904,11 +2950,19 @@ static void btintel_pcie_coredump(struct device *dev)
 	struct  pci_dev *pdev = to_pci_dev(dev);
 	struct btintel_pcie_data *data = pci_get_drvdata(pdev);
 
+	if (!data)
+		return;
+
 	if (test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags))
 		return;
 
 	data->dmp_hdr.trigger_reason  = BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER;
-	queue_work(data->workqueue, &data->rx_work);
+	/* queue_work() returns false if the work is disabled (reset or
+	 * remove in progress); clear the in-progress bit so a later
+	 * trigger can succeed once the work is re-enabled.
+	 */
+	if (!queue_work(data->coredump_workqueue, &data->coredump_work))
+		clear_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, &data->flags);
 }
 #endif
 
@@ -3049,7 +3103,7 @@ static int btintel_pcie_resume(struct device *dev)
 				      &data->flags)) {
 			data->dmp_hdr.trigger_reason =
 				BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
-			queue_work(data->workqueue, &data->rx_work);
+			queue_work(data->coredump_workqueue, &data->coredump_work);
 		}
 		set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
 		btintel_pcie_reset(data->hdev);
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index cae84b00a700..b81f933e2315 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -462,6 +462,8 @@ struct btintel_pcie_dump_header {
  * @workqueue: workqueue for RX work
  * @rx_skb_q: SKB queue for RX packet
  * @rx_work: RX work struct to process the RX packet in @rx_skb_q
+ * @coredump_workqueue: dedicated workqueue for coredump collection
+ * @coredump_work: work struct for coredump trace collection
  * @dma_pool: DMA pool for descriptors, index array and ci
  * @dma_p_addr: DMA address for pool
  * @dma_v_addr: address of pool
@@ -510,6 +512,9 @@ struct btintel_pcie_data {
 	struct work_struct	rx_work;
 	struct work_struct      reset_work;
 
+	struct workqueue_struct	*coredump_workqueue;
+	struct work_struct	coredump_work;
+
 	struct dma_pool	*dma_pool;
 	dma_addr_t	dma_p_addr;
 	void		*dma_v_addr;
-- 
2.54.0


^ permalink raw reply related

* [PATCH BlueZ v2 0/2]Initial Channel Sounding Support for
From: Naga Bhavani Akella @ 2026-06-09 15:45 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

This series adds initial LE Channel Sounding (CS) Initiator support by
introducing the required HCI command flow and event handling for CS
procedures.

The changes include
1. Adding an is_central parameter to validate that
current local role is central before issuing
HCI_LE_CS_Security_Enable command.

2. Introduction of initial LE Channel Sounding (CS)
Initiator support by adding required HCI command flow
and event handling for CS capability discovery,
configuration management, and ranging procedures.

Changes in v2:
profiles/ranging/rap_hci.c :
 - removed unused bt_rap_set_channel_classification
 - removed __maybe_used macro usage


Naga Bhavani Akella (2):
  shared: rap: Check role before sending CS Sec Enable cmd
  profiles: ranging: Add CS Initiator cmd and evt handling

 profiles/ranging/rap.c     |   7 +-
 profiles/ranging/rap_hci.c | 763 ++++++++++++++++++++++++++++++++-----
 src/shared/rap.h           |   3 +-
 3 files changed, 674 insertions(+), 99 deletions(-)

-- 


^ permalink raw reply

* [PATCH BlueZ v2 1/2] shared: rap: Check role before sending CS Sec Enable cmd
From: Naga Bhavani Akella @ 2026-06-09 15:45 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella
In-Reply-To: <20260609154502.3430147-1-naga.akella@oss.qualcomm.com>

Add the is_central parameter to verify whether
the local role is central before sending
the HCI CS Security Enable command.
---
 src/shared/rap.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/shared/rap.h b/src/shared/rap.h
index d3ced61b1..8b4f47757 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -213,5 +213,6 @@ void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
 
 /* Connection handle mapping functions */
 bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
-				const uint8_t *bdaddr, uint8_t bdaddr_type);
+			const uint8_t *bdaddr, uint8_t bdaddr_type,
+			bool is_central);
 void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
-- 


^ permalink raw reply related

* [PATCH BlueZ v2 2/2] profiles: ranging: Add CS Initiator cmd and evt handling
From: Naga Bhavani Akella @ 2026-06-09 15:45 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella
In-Reply-To: <20260609154502.3430147-1-naga.akella@oss.qualcomm.com>

Introduce support for LE Channel Sounding (CS)
ranging procedures in the Initiator role by enabling
required HCI command sequencing and event handling.

Add handling of core HCI LE CS commands and events
This enables cs capability discovery, cs configuration
management and execution of CS ranging procedures
in the Initiator role.
---
 profiles/ranging/rap.c     |   7 +-
 profiles/ranging/rap_hci.c | 763 ++++++++++++++++++++++++++++++++-----
 2 files changed, 672 insertions(+), 98 deletions(-)

diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
index e0a46a87a..eab44b51f 100644
--- a/profiles/ranging/rap.c
+++ b/profiles/ranging/rap.c
@@ -419,9 +419,10 @@ static int rap_accept(struct btd_service *service)
 			DBG("Setting up handle mapping: handle=0x%04X",
 				handle);
 			bt_rap_set_conn_handle(data->hci_sm,
-						data->rap, handle,
-						(const uint8_t *) bdaddr,
-						bdaddr_type);
+					data->rap, handle,
+					(const uint8_t *) bdaddr,
+					bdaddr_type,
+					btd_device_is_initiator(device));
 		} else {
 			error("Failed to find connection handle for device %s",
 				addr);
diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
index febe23384..636471b91 100644
--- a/profiles/ranging/rap_hci.c
+++ b/profiles/ranging/rap_hci.c
@@ -16,11 +16,13 @@
 #include <unistd.h>
 #include <string.h>
 #include <endian.h>
+#include <time.h>
 
 #include "lib/bluetooth/bluetooth.h"
 #include "src/shared/util.h"
 #include "src/shared/queue.h"
 #include "src/shared/rap.h"
+#include "src/shared/att.h"
 #include "src/log.h"
 #include "monitor/bt.h"
 
@@ -67,6 +69,7 @@ struct cs_state_machine {
 	struct bt_rap_hci_cs_options cs_opt;  /* Per-instance CS options */
 	uint8_t role_enable;  /* Role value for HCI commands (1, 2, or 3) */
 	struct queue *conn_mappings;  /* Per-instance connection mappings */
+	struct timespec last_chan_class_time;  /* For 1-second rate limit */
 };
 
 /* Connection Handle Mapping */
@@ -74,10 +77,18 @@ struct rap_conn_mapping {
 	uint16_t handle;
 	uint8_t bdaddr[6];
 	uint8_t bdaddr_type;
+	bool is_central;  /* true if local device is BLE Central on this link */
 	struct bt_att *att;
 	struct bt_rap *rap;
 };
 
+/* Function declarations */
+static bool bt_rap_read_remote_fae_table(void *hci_sm, uint16_t handle);
+static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
+						uint16_t handle);
+static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
+		uint16_t handle);
+
 /* Connection Mapping Helper Functions */
 static void mapping_free(void *data)
 {
@@ -97,14 +108,6 @@ static bool match_mapping_handle(const void *a, const void *b)
 	return mapping->handle == handle;
 }
 
-static bool match_mapping_rap(const void *a, const void *b)
-{
-	const struct rap_conn_mapping *mapping = a;
-	const struct bt_rap *rap = b;
-
-	return mapping->rap == rap;
-}
-
 static struct rap_conn_mapping *find_mapping_by_handle(
 					struct cs_state_machine *sm,
 					uint16_t handle)
@@ -118,19 +121,14 @@ static struct rap_conn_mapping *find_mapping_by_handle(
 
 static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
 				const uint8_t *bdaddr, uint8_t bdaddr_type,
-				struct bt_att *att, struct bt_rap *rap)
+				bool is_central, struct bt_att *att,
+				struct bt_rap *rap)
 {
 	struct rap_conn_mapping *mapping;
 
 	if (!sm)
 		return false;
 
-	if (!sm->conn_mappings) {
-		sm->conn_mappings = queue_new();
-		if (!sm->conn_mappings)
-			return false;
-	}
-
 	/* Check if mapping already exists */
 	mapping = find_mapping_by_handle(sm, handle);
 	if (mapping) {
@@ -138,6 +136,7 @@ static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
 		if (bdaddr)
 			memcpy(mapping->bdaddr, bdaddr, 6);
 		mapping->bdaddr_type = bdaddr_type;
+		mapping->is_central = is_central;
 		mapping->att = att;
 		mapping->rap = rap;
 		return true;
@@ -152,6 +151,7 @@ static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
 	if (bdaddr)
 		memcpy(mapping->bdaddr, bdaddr, 6);
 	mapping->bdaddr_type = bdaddr_type;
+	mapping->is_central = is_central;
 	mapping->att = att;
 	mapping->rap = rap;
 
@@ -171,13 +171,28 @@ static void remove_conn_mapping(struct cs_state_machine *sm, uint16_t handle)
 		mapping_free(mapping);
 }
 
-static void remove_rap_mappings(struct cs_state_machine *sm)
+static struct bt_rap *resolve_handle_to_rap(struct cs_state_machine *sm,
+						uint16_t handle)
 {
-	if (!sm || !sm->conn_mappings)
-		return;
+	struct rap_conn_mapping *mapping;
+
+	if (!sm)
+		return NULL;
+
+	/* Try to find in mapping cache */
+	mapping = find_mapping_by_handle(sm, handle);
+	if (mapping && mapping->rap) {
+		DBG("Found handle 0x%04X in mapping cache", handle);
+		return mapping->rap;
+	}
+
+	/* Profile layer should have called bt_rap_set_conn_handle() during
+	 * connection establishment. If we reach here, the mapping was not set.
+	 */
+	DBG("No mapping found for handle 0x%04X", handle);
+	DBG("Profile layer should call bt_rap_set_conn_handle() on connect");
 
-	queue_remove_all(sm->conn_mappings, match_mapping_rap, sm->rap,
-				mapping_free);
+	return NULL;
 }
 
 /*  State Machine Functions */
@@ -194,6 +209,7 @@ static void cs_state_machine_init(struct cs_state_machine *sm,
 	sm->hci = hci;
 	sm->initiator = false;
 	sm->procedure_active = false;
+	sm->conn_mappings = queue_new();
 
 	/* Store role_enable for HCI commands (1, 2, or 3 from config) */
 	sm->role_enable = role;
@@ -219,8 +235,8 @@ static void cs_set_state(struct cs_state_machine *sm,
 		return;
 
 	/* Validate state values before array access */
-	if (sm->current_state > CS_STATE_UNSPECIFIED ||
-	    new_state > CS_STATE_UNSPECIFIED) {
+	if ((unsigned int)sm->current_state >= ARRAY_SIZE(state_names) ||
+	    (unsigned int)new_state >= ARRAY_SIZE(state_names)) {
 		error("Invalid state transition attempted");
 		return;
 	}
@@ -238,11 +254,85 @@ static enum cs_state cs_get_current_state(struct cs_state_machine *sm)
 	return sm ? sm->current_state : CS_STATE_UNSPECIFIED;
 }
 
+static bool is_initiator_role(const struct cs_state_machine *sm)
+{
+	return sm->role_enable == 0x01 || sm->role_enable == 0x03;
+}
+
+/* Helper function to send read remote capabilities for all connections */
+static void send_read_remote_cap_for_mapping(void *data, void *user_data)
+{
+	struct rap_conn_mapping *mapping = data;
+	struct cs_state_machine *sm = user_data;
+
+	if (!mapping || !sm)
+		return;
+
+	DBG("Sending read remote capabilities for handle 0x%04X",
+		mapping->handle);
+	bt_rap_read_remote_supported_capabilities(sm, mapping->handle);
+}
+
 /* HCI Event Callbacks */
+static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
+					void *user_data)
+{
+	const struct bt_hci_rsp_le_cs_rd_loc_supp_cap *rsp;
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_rsp_le_cs_rd_loc_supp_cap))
+		return;
+
+	DBG("size=0x%02X", size);
+
+	rsp = (const struct bt_hci_rsp_le_cs_rd_loc_supp_cap *) data;
+
+	if (rsp->status != 0) {
+		error("Read Local Supported Capabilities failed: 0x%02X",
+			rsp->status);
+		return;
+	}
+
+	DBG("Local CS Capabilities:");
+	DBG("  Num Config Supported: %u", rsp->num_config_supported);
+	DBG("  Max Consecutive Procedures: %u",
+		rsp->max_consecutive_procedures_supported);
+	DBG("  Num Antennas: %u", rsp->num_antennas_supported);
+	DBG("  Max Antenna Paths: %u", rsp->max_antenna_paths_supported);
+	DBG("  Roles Supported: 0x%02X", rsp->roles_supported);
+	DBG("  Modes Supported: 0x%02X", rsp->modes_supported);
+	DBG("  RTT Capability: 0x%02X", rsp->rtt_capability);
+	DBG("  RTT AA Only N: %u", rsp->rtt_aa_only_n);
+	DBG("  RTT Sounding N: %u", rsp->rtt_sounding_n);
+	DBG("  RTT Random Payload N: %u", rsp->rtt_random_payload_n);
+	DBG("  NADM Sounding Capability: 0x%04X",
+		rsp->nadm_sounding_capability);
+	DBG("  NADM Random Capability: 0x%04X", rsp->nadm_random_capability);
+	DBG("  CS Sync PHYs Supported: 0x%02X", rsp->cs_sync_phys_supported);
+	DBG("  Subfeatures Supported: 0x%04X", rsp->subfeatures_supported);
+	DBG("  T_IP1 Times Supported: 0x%04X", rsp->t_ip1_times_supported);
+	DBG("  T_IP2 Times Supported: 0x%04X", rsp->t_ip2_times_supported);
+	DBG("  T_FCS Times Supported: 0x%04X", rsp->t_fcs_times_supported);
+	DBG("  T_PM Times Supported: 0x%04X", rsp->t_pm_times_supported);
+	DBG("  T_SW Time Supported: %u", rsp->t_sw_time_supported);
+	DBG("  TX SNR Capability: 0x%02X", rsp->tx_snr_capability);
+
+	/* Transition to INIT state before reading remote capabilities */
+	cs_set_state(sm, CS_STATE_INIT);
+
+	/* Send read remote capabilities for all connected devices */
+	if (sm->conn_mappings) {
+		DBG("Sending read remote capabilities for all connections");
+		queue_foreach(sm->conn_mappings,
+				send_read_remote_cap_for_mapping, sm);
+	}
+}
+
 static void rap_def_settings_done_cb(const void *data, uint8_t size,
 					void *user_data)
 {
-	struct bt_hci_rsp_le_cs_set_def_settings *rp;
+	const struct bt_hci_rsp_le_cs_set_def_settings *rp;
 	struct cs_state_machine *sm = user_data;
 
 	if (!sm || !data || size < sizeof(*rp))
@@ -250,10 +340,11 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
 
 	DBG("size=0x%02X", size);
 
-	rp = (struct bt_hci_rsp_le_cs_set_def_settings *) data;
+	rp = (const struct bt_hci_rsp_le_cs_set_def_settings *) data;
 
-	if (cs_get_current_state(sm) != CS_STATE_INIT) {
-		DBG("Event received in Wrong State!! Expected : CS_STATE_INIT");
+	if (cs_get_current_state(sm) == CS_STATE_STOPPED ||
+	    cs_get_current_state(sm) == CS_STATE_UNSPECIFIED) {
+		DBG("Def settings response in terminal state, ignoring");
 		return;
 	}
 
@@ -261,9 +352,13 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
 		/* Success - proceed to configuration */
 		cs_set_state(sm, CS_STATE_WAIT_CONFIG_CMPLT);
 
-		/* Reflector role */
-		DBG("Waiting for CS Config Completed event...");
-		/* TODO: Initiator role - Send CS Config complete cmd */
+		/* If role is initiator, send CS Create Config command */
+		if (is_initiator_role(sm)) {
+			rap_send_hci_cs_create_config_command(sm, rp->handle);
+		} else {
+			/* Reflector role */
+			DBG("Reflector role: Waiting for CS Config Completed");
+		}
 	} else {
 		/* Error - transition to stopped */
 		error("CS Set default setting failed with status 0x%02X",
@@ -272,8 +367,196 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
 	}
 }
 
-static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
+static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
 						uint16_t handle)
+{
+	struct bt_hci_cmd_le_cs_create_config cmd;
+	unsigned int status;
+
+	uint8_t channel_map[10] = {
+		0xFC, 0xFF, 0x7F, 0xFC, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0x1F
+	};
+
+	if (!sm || !sm->hci) {
+		error("CS Create Config: sm or hci is null");
+		return;
+	}
+
+	DBG("Sending CS Create Config command for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+	cmd.create_context = 1;
+	/* Default values, will change to pick user given values later */
+	cmd.config_id              = 0x00;
+	cmd.main_mode_type         = 0x01;
+	cmd.sub_mode_type          = 0xFF;
+	cmd.min_main_mode_steps    = 0x02;
+	cmd.max_main_mode_steps    = 0x03;
+	cmd.main_mode_repetition   = 0x01;
+	cmd.mode_0_steps           = 0x02;
+	cmd.role                   = 0x00;
+	cmd.rtt_type               = 0x00;
+	cmd.cs_sync_phy            = 0x01;
+	memcpy(cmd.channel_map, channel_map, 10);
+	cmd.channel_map_repetition = 0x01;
+	cmd.channel_selection_type = 0x00;
+	cmd.ch3c_shape             = 0x00;
+	cmd.ch3c_jump              = 0x02;
+	cmd.reserved               = 0x00;
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_CREATE_CONFIG,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send CS Create Config command");
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	DBG("CS Create Config command sent successfully");
+}
+
+static void rap_send_hci_cs_remove_config_command(struct cs_state_machine *sm,
+						uint16_t handle)
+{
+	struct bt_hci_cmd_le_cs_remove_config cmd;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("CS Remove Config: sm or hci is null");
+		return;
+	}
+
+	DBG("Sending CS Remove Config command for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+	cmd.config_id = 0x00;  /* Default config ID */
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_REMOVE_CONFIG,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send CS Remove Config command");
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	DBG("CS Remove Config command sent successfully");
+}
+
+static void rap_send_hci_cs_security_enable_command(
+		struct cs_state_machine *sm, uint16_t handle)
+{
+	struct bt_hci_cmd_le_cs_sec_enable cmd;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("CS Security Enable: sm or hci is null");
+		return;
+	}
+
+	DBG("Sending CS Security Enable command for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SEC_ENABLE,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send CS Security Enable command");
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	DBG("CS Security Enable command sent successfully");
+}
+
+static bool rap_send_hci_cs_set_procedure_parameters(
+		struct cs_state_machine *sm, uint16_t handle)
+{
+	struct bt_hci_cmd_le_cs_set_proc_params cmd;
+	unsigned int status;
+	uint8_t min_sub_event_len[3] = {
+		0x00, 0x20, 0x00
+	};
+
+	uint8_t max_sub_event_len[3] = {
+		0x03, 0x20, 0x00
+	};
+
+	if (!sm || !sm->hci) {
+		error("CS Set Procedure Parameters: sm or hci is null");
+		return false;
+	}
+
+	DBG("Sending CS Set Procedure Parameters for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+	/* Default values, will change to pick user given values later */
+	cmd.config_id = 0x00;
+	cmd.max_procedure_len = 0x0640;
+	cmd.min_procedure_interval = 0x1E;
+	cmd.max_procedure_interval = 0x96;
+	cmd.max_procedure_count = 0x00;
+	memcpy(cmd.min_subevent_len, min_sub_event_len, 3);
+	memcpy(cmd.max_subevent_len, max_sub_event_len, 3);
+	cmd.tone_antenna_config_selection = 0x07;
+	cmd.phy                    = 0x01;
+	cmd.tx_power_delta         = 0x80;
+	cmd.preferred_peer_antenna = 0x03;
+	cmd.snr_control_initiator  = 0xFF;
+	cmd.snr_control_reflector  = 0xFF;
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_PROC_PARAMS,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send CS Set Procedure Parameters command");
+		return false;
+	}
+
+	DBG("CS Set Procedure Parameters command sent successfully");
+	return true;
+}
+
+static bool rap_send_hci_cs_procedure_enable(struct cs_state_machine *sm,
+						uint16_t handle,
+						bool enable_proc)
+{
+	struct bt_hci_cmd_le_cs_proc_enable cmd;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("CS Procedure Enable: sm or hci is null");
+		return false;
+	}
+
+	DBG("Sending CS Procedure Enable for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+	cmd.config_id = 0x00; /* Default config Id */
+	cmd.enable = enable_proc ? 0x01 : 0x00;
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_PROC_ENABLE,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send CS Procedure Enable command");
+		return false;
+	}
+
+	DBG("CS Procedure Enable command sent successfully");
+	return true;
+}
+
+static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
+		const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *ev)
 {
 	struct bt_hci_cmd_le_cs_set_def_settings cp;
 	unsigned int status;
@@ -285,8 +568,8 @@ static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
 
 	memset(&cp, 0, sizeof(cp));
 
-	if (handle)
-		cp.handle = handle;
+	if (ev->handle)
+		cp.handle = ev->handle;
 
 	cp.role_enable = sm->role_enable;  /* Use preserved HCI command value */
 	cp.cs_sync_antenna_selection = sm->cs_opt.cs_sync_ant_sel;
@@ -302,12 +585,93 @@ static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
 		error("Failed to send default settings cmd");
 }
 
+static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
+				      void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	const struct bt_hci_evt_le_cs_rd_rem_fae_complete *evt;
+	struct iovec iov;
+	int i;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_rd_rem_fae_complete))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	/* Pull the entire structure at once */
+	evt = util_iov_pull_mem(&iov, sizeof(*evt));
+
+	if (!evt) {
+		error("Failed to pull remote FAE complete struct");
+		return;
+	}
+
+	DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
+
+	/* Check status */
+	if (evt->status != 0) {
+		/* Status 0x11 (Unsupported Feature or Parameter Value) means
+		 * the remote has zero FAE, the procedure continues
+		 * to the Default Settings step.
+		 */
+		if (evt->status == 0x11) {
+			DBG("Remote FAE=0 (No_FAE), proceed to Def Settings");
+			if (is_initiator_role(sm)) {
+				struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete
+								tmp_ev;
+
+				memset(&tmp_ev, 0, sizeof(tmp_ev));
+				tmp_ev.handle = evt->handle;
+				DBG("Initiator: send def settings (No_FAE)");
+				rap_send_hci_def_settings_command(sm, &tmp_ev);
+			} else {
+				DBG("Reflector role: continuing after No_FAE");
+				cs_set_state(sm, CS_STATE_INIT);
+			}
+			return;
+		}
+		error("Remote FAE Table read failed with status 0x%02X",
+			evt->status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	DBG("Remote FAE Table received:");
+	for (i = 0; i < 72; i += 8) {
+		DBG("  [%02d-%02d]: %02X %02X %02X %02X %02X %02X %02X %02X",
+			i, i+7,
+			evt->remote_fae_table[i], evt->remote_fae_table[i+1],
+			evt->remote_fae_table[i+2], evt->remote_fae_table[i+3],
+			evt->remote_fae_table[i+4], evt->remote_fae_table[i+5],
+			evt->remote_fae_table[i+6], evt->remote_fae_table[i+7]);
+	}
+
+	/* After receiving FAE Table, send default settings */
+	/* Local capabilities already read before this event */
+	if (is_initiator_role(sm)) {
+		struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete tmp_ev;
+
+		memset(&tmp_ev, 0, sizeof(tmp_ev));
+		tmp_ev.handle = evt->handle;
+		DBG("Initiator role: send def settings after FAE table");
+		rap_send_hci_def_settings_command(sm, &tmp_ev);
+	} else {
+		DBG("Reflector role: Proceeding after FAE Table");
+		cs_set_state(sm, CS_STATE_INIT);
+	}
+}
+
 static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 					   void *user_data)
 {
 	struct cs_state_machine *sm = user_data;
 	const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *evt;
+	struct bt_rap *rap;
 	struct iovec iov;
+	uint16_t subfeatures_supported;
 
 	if (!sm || !data || size < sizeof(*evt))
 		return;
@@ -334,6 +698,16 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 		return;
 	}
 
+	/* Resolve handle to RAP instance */
+	rap = resolve_handle_to_rap(sm, evt->handle);
+
+	if (!rap) {
+		DBG("[WARN] Could not resolve handle 0x%04X to RAP instance",
+			evt->handle);
+		/* Continue with state machine RAP for now */
+		rap = sm->rap;
+	}
+
 	DBG("num_config=%u, ",
 		evt->num_config_supported);
 	DBG("max_consecutive_proc=%u, num_antennas=%u, ",
@@ -343,9 +717,26 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 		evt->max_antenna_paths_supported,
 		evt->roles_supported,
 		evt->modes_supported);
+	subfeatures_supported = le16_to_cpu(evt->subfeatures_supported);
+	DBG("subfeatures_supported=0x%04X", subfeatures_supported);
 
-	rap_send_hci_def_settings_command(sm, evt->handle);
-	cs_set_state(sm, CS_STATE_INIT);
+	/* Check Bit 1 of subfeatures_supported (0x0002) */
+	if (!(subfeatures_supported & 0x0002)) {
+		DBG("Bit 1 not set, sending Read Remote FAE Table");
+		bt_rap_read_remote_fae_table(sm, evt->handle);
+		return;
+	}
+
+	/* Local capabilities already read before this event */
+	if (is_initiator_role(sm)) {
+		DBG("Initiator role: send def settings cmd for handle 0x%04X",
+			evt->handle);
+		rap_send_hci_def_settings_command(sm, evt);
+	} else {
+		DBG("Reflector role: send def settings cmd");
+		cs_set_state(sm, CS_STATE_INIT);
+		rap_send_hci_def_settings_command(sm, evt);
+	}
 }
 
 static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
@@ -383,8 +774,15 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
 
 	/* Check status */
 	if (evt->status != 0) {
-		error("Configuration failed with status 0x%02X",
-			evt->status);
+		if (evt->action != 0x00) {
+			/* Create/update failed — try to remove the config */
+			error("Configuration failed with status 0x%02X",
+				evt->status);
+			rap_send_hci_cs_remove_config_command(sm, evt->handle);
+		} else {
+			error("CS Config Remove failed with status 0x%02X",
+				evt->status);
+		}
 		cs_set_state(sm, CS_STATE_STOPPED);
 		return;
 	}
@@ -428,12 +826,40 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
 		rap_ev.main_mode_type, rap_ev.sub_mode_type,
 		rap_ev.role, rap_ev.rtt_type);
 
+	if (rap_ev.action == 0x00) {
+		cs_set_state(sm, CS_STATE_UNSPECIFIED);
+		DBG("CS Config Removed !!!");
+		bt_rap_hci_cs_config_complete_callback(size, &rap_ev, sm->rap);
+		return;
+	}
 	/* Success - proceed to Security enable complete */
 	cs_set_state(sm, CS_STATE_WAIT_SEC_CMPLT);
 
-	/* Reflector role */
-	DBG("Waiting for security enable event...");
-	/* TODO: Initiator role - Send CS Security enable cmd */
+	/* CS Security Enable may only be issued by the BLE Central */
+	if (rap_ev.role == 0x00) {
+		/* Initiator role */
+		struct rap_conn_mapping *mapping;
+
+		mapping = find_mapping_by_handle(sm, evt->handle);
+		if (!mapping || !mapping->is_central) {
+			error("CS Security Enable skipped: not BLE Central");
+			cs_set_state(sm, CS_STATE_STOPPED);
+			return;
+		}
+
+		if (bt_att_get_security(mapping->att, NULL) <
+						BT_ATT_SECURITY_MEDIUM) {
+			error("CS Security Enable skipped: not encrypted");
+			cs_set_state(sm, CS_STATE_STOPPED);
+			return;
+		}
+
+		DBG("Central,encrypted: Sending CS Security Enable command");
+		rap_send_hci_cs_security_enable_command(sm, evt->handle);
+	} else {
+		/* Reflector role */
+		DBG("Reflector role: Waiting for security enable event...");
+	}
 
 	/* Send callback to RAP Profile */
 	bt_rap_hci_cs_config_complete_callback(size, &rap_ev, sm->rap);
@@ -486,9 +912,27 @@ static void rap_cs_sec_enable_cmplt_evt(const void *data, uint8_t size,
 		/* Success - proceed to configuration */
 		cs_set_state(sm, CS_STATE_WAIT_PROC_CMPLT);
 
-		/* Reflector role */
-		DBG("Waiting for CS Proc complete event...");
-		/* TODO: Initiator - Send CS Proc Set Parameter and enable */
+		/* Check if role is initiator */
+		if (sm->cs_opt.role == CS_INITIATOR) {
+			DBG("Initiator role: Sending CS Set Procedure Params");
+			if (!rap_send_hci_cs_set_procedure_parameters(
+							sm, handle)) {
+				error("Failed to send CS Set Procedure Params");
+				cs_set_state(sm, CS_STATE_STOPPED);
+				return;
+			}
+
+			DBG("Initiator role: Sending CS Procedure Enable");
+			if (!rap_send_hci_cs_procedure_enable(sm, handle,
+								      true)) {
+				error("Failed to send CS Procedure Enable");
+				cs_set_state(sm, CS_STATE_STOPPED);
+				return;
+			}
+		} else {
+			// Reflector role
+			DBG("Reflector role: Waiting for CS Proc compl event");
+		}
 	} else {
 		/* Error - transition to stopped */
 		error("Security enable failed with status 0x%02X",
@@ -565,8 +1009,13 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
 		rap_ev.proc_intrvl);
 
 	/* Success - procedure started */
-	cs_set_state(sm, CS_STATE_STARTED);
-	sm->procedure_active = true;
+	if (rap_ev.state == 0x01) {
+		cs_set_state(sm, CS_STATE_STARTED);
+		sm->procedure_active = true;
+	} else if (rap_ev.state == 0x00) {
+		cs_set_state(sm, CS_STATE_STOPPED);
+		sm->procedure_active = false;
+	}
 
 	/* Send callback to RAP Profile */
 	bt_rap_hci_cs_procedure_enable_complete_callback(size,
@@ -800,6 +1249,54 @@ static void parse_cs_step(struct iovec *iov, struct cs_step_data *step,
 	}
 }
 
+/*
+ * Handle the common step-parsing tail shared by both subevent result variants.
+ * Fixes truncation (num_steps_reported > CS_MAX_STEPS) by zeroing the step
+ * count and trimming send_len to header_size, matching the abort-status path.
+ */
+static void cs_parse_steps(struct iovec *iov,
+			uint8_t num_steps_reported,
+			uint8_t proc_done_status,
+			uint8_t subevt_done_status,
+			uint8_t abort_reason,
+			uint8_t cs_role, uint8_t cs_rtt_type,
+			uint8_t max_paths,
+			struct cs_step_data *step_data,
+			uint8_t *num_steps_out,
+			size_t *send_len,
+			size_t header_size)
+{
+	uint8_t steps = MIN(num_steps_reported, CS_MAX_STEPS);
+	uint8_t i;
+
+	if (num_steps_reported > CS_MAX_STEPS) {
+		DBG("Too many steps reported: %u (max %u)",
+			num_steps_reported, CS_MAX_STEPS);
+		*num_steps_out = 0;
+		*send_len = header_size;
+		return;
+	}
+
+	if (subevt_done_status == 0xF || proc_done_status == 0xF) {
+		DBG("CS Procedure/Subevent aborted: ");
+		DBG("sub evt status = %d, proc status = %d, reason = %d",
+			subevt_done_status, proc_done_status, abort_reason);
+		/*
+		 * Step bytes were never parsed; zero-initialised step_data[]
+		 * entries would appear as spurious mode-0 quality=0 steps to
+		 * the BCS algorithm.  Clear the count so an aborted subevent
+		 * carries no fake measurements.
+		 */
+		*num_steps_out = 0;
+		*send_len = header_size;
+		return;
+	}
+
+	for (i = 0; i < steps; i++)
+		parse_cs_step(iov, &step_data[i], cs_role, cs_rtt_type,
+			max_paths);
+}
+
 static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
 				void *user_data)
 {
@@ -822,7 +1319,6 @@ static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
 	uint8_t abort_reason;
 	uint8_t num_ant_paths;
 	uint8_t num_steps_reported;
-	uint8_t i;
 
 	if (!sm || !data ||
 		size < sizeof(struct bt_hci_evt_le_cs_subevent_result))
@@ -883,28 +1379,13 @@ static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
 	rap_ev->num_ant_paths                = num_ant_paths;
 	rap_ev->num_steps_reported           = steps;
 
-	if (num_steps_reported > CS_MAX_STEPS) {
-		DBG("Too many steps reported: %u (max %u)",
-			num_steps_reported, CS_MAX_STEPS);
-		goto send_event;
-	}
-
-	/* Early exit for error conditions */
-	if (rap_ev->subevt_done_status == 0xF ||
-	    rap_ev->proc_done_status == 0xF) {
-		DBG("CS Procedure/Subevent aborted: ");
-		DBG("sub evt status = %d, proc status = %d, reason = %d",
-			rap_ev->subevt_done_status, rap_ev->proc_done_status,
-			rap_ev->abort_reason);
-		goto send_event;
-	}
+	cs_parse_steps(&iov, num_steps_reported,
+			proc_done_status, subevt_done_status, abort_reason,
+			cs_role, cs_rtt_type, max_paths,
+			rap_ev->step_data, &rap_ev->num_steps_reported,
+			&send_len,
+			offsetof(struct rap_ev_cs_subevent_result, step_data));
 
-	/* Parse interleaved step data from remaining iovec data */
-	for (i = 0; i < steps; i++)
-		parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
-			max_paths);
-
-send_event:
 	DBG("CS subevent result processed: %zu bytes, ", send_len);
 	bt_rap_hci_cs_subevent_result_callback(send_len, rap_ev, sm->rap);
 	free(rap_ev);
@@ -920,7 +1401,7 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
 	uint8_t cs_rtt_type;
 	uint8_t max_paths;
 	uint8_t steps;
-	size_t send_len = 0;
+	size_t send_len;
 	uint16_t handle;
 	uint8_t config_id;
 	uint8_t proc_done_status;
@@ -928,7 +1409,6 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
 	uint8_t abort_reason;
 	uint8_t num_ant_paths;
 	uint8_t num_steps_reported;
-	uint8_t i;
 
 	if (!sm || !data ||
 		size < sizeof(struct bt_hci_evt_le_cs_subevent_result_continue))
@@ -981,28 +1461,14 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
 	rap_ev->num_ant_paths                = num_ant_paths;
 	rap_ev->num_steps_reported           = steps;
 
-	if (num_steps_reported > CS_MAX_STEPS) {
-		DBG("Too many steps reported: %u (max %u)",
-			num_steps_reported, CS_MAX_STEPS);
-		goto send_event;
-	}
-
-	/* Early exit for error conditions */
-	if (rap_ev->subevt_done_status == 0xF ||
-	    rap_ev->proc_done_status == 0xF) {
-		DBG("CS Procedure/Subevent aborted: ");
-		DBG("sub evt status = %d, proc status = %d, reason = %d",
-			rap_ev->subevt_done_status, rap_ev->proc_done_status,
-			rap_ev->abort_reason);
-		goto send_event;
-	}
+	cs_parse_steps(&iov, num_steps_reported,
+			proc_done_status, subevt_done_status, abort_reason,
+			cs_role, cs_rtt_type, max_paths,
+			rap_ev->step_data, &rap_ev->num_steps_reported,
+			&send_len,
+			offsetof(struct rap_ev_cs_subevent_result_cont,
+							step_data));
 
-	/* Parse interleaved step data from remaining iovec data */
-	for (i = 0; i < steps; i++)
-		parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
-			max_paths);
-
-send_event:
 	DBG("CS subevent result cont processed: %zu bytes, ", send_len);
 	bt_rap_hci_cs_subevent_result_cont_callback(send_len, rap_ev, sm->rap);
 	free(rap_ev);
@@ -1010,6 +1476,96 @@ send_event:
 
 /* Subevent handler function type */
 
+/* Set Ch Class cmd handling to be added after DBus support enabled */
+
+static bool bt_rap_read_remote_fae_table(void *hci_sm, uint16_t handle)
+{
+	struct cs_state_machine *sm = hci_sm;
+	struct bt_hci_cmd_le_cs_rd_rem_fae cmd;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("Invalid state machine or HCI");
+		return false;
+	}
+
+	DBG("Sending Read Remote FAE Table for handle 0x%04X", handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_REM_FAE,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send Read Remote FAE Table command");
+		return false;
+	}
+
+	DBG("Read Remote FAE Table command sent successfully");
+	return true;
+}
+
+/* This cmd is used by host to start cs distance measurement procedure
+ * function will be used when user start distance measurement
+ * keeping it unused till DBUS API is added
+ */
+static bool bt_rap_read_local_supported_capabilities(
+		void *hci_sm)
+{
+	struct cs_state_machine *sm = hci_sm;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("Invalid state machine or HCI");
+		return false;
+	}
+
+	DBG("Sending Read Local Supported Capabilities command");
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_LOC_SUPP_CAP,
+				NULL, 0, rap_rd_loc_supp_cap_done_cb,
+				sm, NULL);
+
+	if (!status) {
+		error("Failed to send Read Local Supported Capabilities");
+		return false;
+	}
+
+	DBG("Read Local Supported Capabilities command sent successfully");
+	return true;
+}
+
+static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
+		uint16_t handle)
+{
+	struct cs_state_machine *sm = hci_sm;
+	struct bt_hci_cmd_le_cs_rd_rem_supp_cap cmd;
+	unsigned int status;
+
+	if (!sm || !sm->hci) {
+		error("Invalid state machine or HCI");
+		return false;
+	}
+
+	DBG("Sending Read Remote Supported Capabilities for handle 0x%04X",
+		handle);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.handle = cpu_to_le16(handle);
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_REM_SUPP_CAP,
+				&cmd, sizeof(cmd), NULL, sm, NULL);
+
+	if (!status) {
+		error("Failed to send Read Remote Capabilities command");
+		return false;
+	}
+
+	DBG("Read Remote Capabilities command sent successfully");
+	return true;
+}
+
 static void unregister_event_id(void *data, void *user_data)
 {
 	struct bt_hci *hci = user_data;
@@ -1040,7 +1596,15 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
 	cs_state_machine_init(sm, rap, hci, role, cs_sync_ant_sel,
 				max_tx_power);
 
+	/* place holder, need DBus API to be called */
+	bt_rap_read_local_supported_capabilities(sm);
+
 	sm->event_ids = queue_new();
+	if (!sm->event_ids) {
+		error("Failed to allocate event_ids queue");
+		free(sm);
+		return NULL;
+	}
 
 	/* Register each LE Meta subevent individually */
 	id = bt_hci_register_subevent(hci,
@@ -1051,6 +1615,15 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
 
 	queue_push_tail(sm->event_ids, UINT_TO_PTR(id));
 
+	id = bt_hci_register_subevent(hci,
+		BT_HCI_EVT_LE_CS_RD_REM_FAE_COMPLETE,
+		rap_rd_rem_fae_cmplt_evt, sm, NULL);
+
+	if (!id)
+		goto fail;
+
+	queue_push_tail(sm->event_ids, UINT_TO_PTR(id));
+
 	id = bt_hci_register_subevent(hci,
 			BT_HCI_EVT_LE_CS_CONFIG_COMPLETE,
 			rap_cs_config_cmplt_evt, sm, NULL);
@@ -1100,12 +1673,14 @@ fail:
 	error("Failed to register hci le meta subevents");
 	queue_foreach(sm->event_ids, unregister_event_id, hci);
 	queue_destroy(sm->event_ids, NULL);
+	queue_destroy(sm->conn_mappings, mapping_free);
 	free(sm);
 	return NULL;
 }
 
-bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
-				const uint8_t *bdaddr, uint8_t bdaddr_type)
+bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap,
+		uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
+		bool is_central)
 {
 	struct cs_state_machine *sm = hci_sm;
 	struct bt_att *att;
@@ -1124,7 +1699,8 @@ bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
 			bdaddr[2], bdaddr[1], bdaddr[0], bdaddr_type);
 	}
 
-	return add_conn_mapping(sm, handle, bdaddr, bdaddr_type, att, rap);
+	return add_conn_mapping(sm, handle, bdaddr, bdaddr_type, is_central,
+				att, rap);
 }
 
 void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle)
@@ -1157,9 +1733,6 @@ void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm)
 		queue_destroy(sm->event_ids, NULL);
 
 		/* Clean up per-instance connection mappings */
-		remove_rap_mappings(sm);
-
-		/* Destroy the connection mappings queue */
 		queue_destroy(sm->conn_mappings, mapping_free);
 
 		/* Free the state machine */
-- 


^ permalink raw reply related

* Re: [PATCH BlueZ 1/2] avdtp: Add GetConfiguration DBus function
From: Simon Mikuda @ 2026-06-09 16:13 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <CABBYNZKkiRaDQFUrMp65=DF-37gBnSJ0H__bj-iA4Nx2Y=Yhkw@mail.gmail.com>

Yes, the configuration is exposed, but it is the configuration we 
already selected or accepted.
You are right, it is to pass PTS test cases, but i think there are 2 
valid points:
1. Provide API completeness
2. Theoretically, it is "useful", you could GetConfiguration of other 
endpoints that are configured from other remote device

You are right, this API certainly won't be used and has no valid use 
case, so if this is not merged it is no big deal. But you can pass PTS 
test cases, and you can retrieve "fresh" configuration from remote devices.

It can be hidden by some configuration, but i have no idea how. Can you 
give some pointers on this?


On 6/8/26 15:59, Luiz Augusto von Dentz wrote:
> Hi Simon,
>
> On Mon, Jun 8, 2026 at 8:15 AM Simon Mikuda
> <simon.mikuda@streamunlimited.com> wrote:
>> Change avdtp_get_configuration to accept remote SEP instead of stream a
>> and add an async callback for response/reject handling; the old function
>> was not used anywhere (unit-test just copied signature), so it was
>> changed in place.
>>
>> Add a GetConfiguration async D-Bus method to the remote SEP interface that
>> returns the retrieved configuration as a byte array.
>>
>> This can be used to pass: AVDTP/SRC/INT/SIG/SMG/BV-11-C
>> ---
>>   profiles/audio/a2dp.c  | 63 ++++++++++++++++++++++++++++++++++
>>   profiles/audio/avdtp.c | 76 ++++++++++++++++++++++++++++++++++++++++--
>>   profiles/audio/avdtp.h |  9 ++++-
>>   3 files changed, 144 insertions(+), 4 deletions(-)
>>
>> diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
>> index c7e0fc75c..8dcf510ca 100644
>> --- a/profiles/audio/a2dp.c
>> +++ b/profiles/audio/a2dp.c
>> @@ -2168,11 +2168,74 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
>>          return NULL;
>>   }
>>
>> +static void get_configuration_cb(struct avdtp *session, GSList *caps,
>> +                               struct avdtp_error *err, void *user_data)
>> +{
>> +       DBusMessage *msg = user_data;
>> +       DBusMessage *reply;
>> +       DBusMessageIter iter, array;
>> +       GSList *l;
>> +
>> +       if (err) {
>> +               reply = btd_error_failed(msg, "GetConfiguration rejected");
>> +               goto done;
>> +       }
>> +
>> +       reply = dbus_message_new_method_return(msg);
>> +
>> +       dbus_message_iter_init_append(reply, &iter);
>> +       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
>> +                                       DBUS_TYPE_BYTE_AS_STRING, &array);
>> +
>> +       for (l = caps; l; l = g_slist_next(l)) {
>> +               struct avdtp_service_capability *cap = l->data;
>> +               uint8_t *ptr = (uint8_t *) cap;
>> +               int i;
>> +
>> +               for (i = 0; i < cap->length + 2; i++)
>> +                       dbus_message_iter_append_basic(&array,
>> +                                                       DBUS_TYPE_BYTE,
>> +                                                       &ptr[i]);
>> +       }
>> +
>> +       dbus_message_iter_close_container(&iter, &array);
>> +
>> +done:
>> +       g_dbus_send_message(btd_get_dbus_connection(), reply);
>> +       dbus_message_unref(msg);
>> +       g_slist_free_full(caps, g_free);
>> +}
>> +
>> +static DBusMessage *get_configuration(DBusConnection *conn,
>> +                                               DBusMessage *msg, void *data)
>> +{
>> +       struct a2dp_remote_sep *rsep = data;
>> +       struct a2dp_channel *chan = rsep->chan;
>> +       int err;
>> +
>> +       if (chan->session == NULL)
>> +               return btd_error_not_available(msg);
>> +
>> +       err = avdtp_get_configuration(chan->session, rsep->sep,
>> +                                       get_configuration_cb,
>> +                                       dbus_message_ref(msg));
>> +       if (err < 0) {
>> +               dbus_message_unref(msg);
>> +               return btd_error_failed(msg, strerror(-err));
>> +       }
>> +
>> +       return NULL;
>> +}
>> +
>>   static const GDBusMethodTable sep_methods[] = {
>>          { GDBUS_ASYNC_METHOD("SetConfiguration",
>>                                          GDBUS_ARGS({ "endpoint", "o" },
>>                                                  { "properties", "a{sv}" } ),
>>                                          NULL, set_configuration) },
>> +       { GDBUS_ASYNC_METHOD("GetConfiguration",
>> +                                       NULL,
>> +                                       GDBUS_ARGS({ "configuration", "ay" }),
>> +                                       get_configuration) },
> The configuration is already exposed via MediaTransport properties:
>
> https://github.com/bluez/bluez/blob/master/doc/org.bluez.MediaTransport.rst#arraybyte-configuration-readonly
>
> So if this is only to satisfy PTS, maybe it should be hidden behind
> main.conf:Testing, that said we do have a dedicated tool for passing
> PTS testing, avdtptest, and if you had grepped for
> AVDTP/SRC/INT/SIG/SMG/BV-11-C you would have found it is documented in
> avdtp-pts.rst:
>
> https://github.com/bluez/bluez/blob/7a0c8ebf91e69c3b2d2d48f8f4b0074fddde1fa4/doc/qualification/avdtp-pts.rst#L258
>
>>          { },
>>   };
>>
>> diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
>> index d475a545e..5f0424c81 100644
>> --- a/profiles/audio/avdtp.c
>> +++ b/profiles/audio/avdtp.c
>> @@ -348,6 +348,11 @@ struct discover_callback {
>>          void *user_data;
>>   };
>>
>> +struct getconf_callback {
>> +       avdtp_get_configuration_cb_t cb;
>> +       void *user_data;
>> +};
>> +
>>   struct avdtp_stream {
>>          GIOChannel *io;
>>          uint16_t imtu;
>> @@ -406,6 +411,7 @@ struct avdtp {
>>          char *buf;
>>
>>          struct discover_callback *discover;
>> +       struct getconf_callback *getconf;
>>          struct pending_req *req;
>>
>>          unsigned int dc_timer;
>> @@ -1093,6 +1099,24 @@ static void finalize_discovery(struct avdtp *session, int err)
>>          session->discover = NULL;
>>   }
>>
>> +static void finalize_getconf(struct avdtp *session, int err)
>> +{
>> +       struct getconf_callback *getconf = session->getconf;
>> +       struct avdtp_error avdtp_err;
>> +
>> +       if (!getconf)
>> +               return;
>> +
>> +       session->getconf = NULL;
>> +
>> +       avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
>> +
>> +       if (getconf->cb)
>> +               getconf->cb(session, NULL, &avdtp_err, getconf->user_data);
>> +
>> +       g_free(getconf);
>> +}
>> +
>>   static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
>>   {
>>          struct avdtp_local_sep *sep = stream->lsep;
>> @@ -1144,6 +1168,7 @@ static void avdtp_free(void *data)
>>          g_slist_free_full(session->seps, sep_free);
>>
>>          g_free(session->buf);
>> +       g_free(session->getconf);
>>
>>          btd_device_unref(session->device);
>>          g_free(session);
>> @@ -1162,6 +1187,7 @@ static void connection_lost(struct avdtp *session, int err)
>>          session->streams = NULL;
>>
>>          finalize_discovery(session, err);
>> +       finalize_getconf(session, err);
>>
>>          avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
>>
>> @@ -3008,6 +3034,20 @@ static gboolean avdtp_parse_resp(struct avdtp *session,
>>                                  next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
>>                          finalize_discovery(session, 0);
>>                  return TRUE;
>> +       case AVDTP_GET_CONFIGURATION:
>> +               DBG("GET_CONFIGURATION request succeeded");
>> +               if (session->getconf) {
>> +                       struct getconf_callback *cb = session->getconf;
>> +                       GSList *caps;
>> +
>> +                       session->getconf = NULL;
>> +                       caps = caps_to_list(((struct getcap_resp *) buf)->caps,
>> +                                       size - sizeof(struct getcap_resp),
>> +                                       NULL, NULL, NULL);
>> +                       cb->cb(session, caps, NULL, cb->user_data);
>> +                       g_free(cb);
>> +               }
>> +               return TRUE;
>>          }
>>
>>          /* The remaining commands require an existing stream so bail out
>> @@ -3115,6 +3155,19 @@ static gboolean avdtp_parse_rej(struct avdtp *session,
>>                  error("GET_CAPABILITIES request rejected: %s (%d)",
>>                                  avdtp_strerror(&err), err.err.error_code);
>>                  return TRUE;
>> +       case AVDTP_GET_CONFIGURATION:
>> +               if (!seid_rej_to_err(buf, size, &err))
>> +                       return FALSE;
>> +               error("GET_CONFIGURATION request rejected: %s (%d)",
>> +                               avdtp_strerror(&err), err.err.error_code);
>> +               if (session->getconf) {
>> +                       struct getconf_callback *cb = session->getconf;
>> +
>> +                       session->getconf = NULL;
>> +                       cb->cb(session, NULL, &err, cb->user_data);
>> +                       g_free(cb);
>> +               }
>> +               return TRUE;
>>          case AVDTP_OPEN:
>>                  if (!seid_rej_to_err(buf, size, &err))
>>                          return FALSE;
>> @@ -3536,18 +3589,35 @@ unsigned int avdtp_stream_add_cb(struct avdtp *session,
>>          return stream_cb->id;
>>   }
>>
>> -int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
>> +int avdtp_get_configuration(struct avdtp *session,
>> +                                       struct avdtp_remote_sep *rsep,
>> +                                       avdtp_get_configuration_cb_t cb,
>> +                                       void *user_data)
>>   {
>>          struct seid_req req;
>> +       int err;
>>
>>          if (session->state < AVDTP_SESSION_STATE_CONNECTED)
>>                  return -EINVAL;
>>
>> +       if (session->getconf)
>> +               return -EBUSY;
>> +
>>          memset(&req, 0, sizeof(req));
>> -       req.acp_seid = stream->rseid;
>> +       req.acp_seid = rsep->seid;
>>
>> -       return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
>> +       err = send_request(session, FALSE, NULL, AVDTP_GET_CONFIGURATION,
>>                                                          &req, sizeof(req));
>> +       if (err < 0)
>> +               return err;
>> +
>> +       if (cb) {
>> +               session->getconf = g_new0(struct getconf_callback, 1);
>> +               session->getconf->cb = cb;
>> +               session->getconf->user_data = user_data;
>> +       }
>> +
>> +       return 0;
>>   }
>>
>>   static void copy_capabilities(gpointer data, gpointer user_data)
>> diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
>> index 10c8f496b..45c54086f 100644
>> --- a/profiles/audio/avdtp.h
>> +++ b/profiles/audio/avdtp.h
>> @@ -267,8 +267,15 @@ int avdtp_set_configuration(struct avdtp *session,
>>                                  GSList *caps,
>>                                  struct avdtp_stream **stream);
>>
>> +typedef void (*avdtp_get_configuration_cb_t)(struct avdtp *session,
>> +                                               GSList *caps,
>> +                                               struct avdtp_error *err,
>> +                                               void *user_data);
>> +
>>   int avdtp_get_configuration(struct avdtp *session,
>> -                               struct avdtp_stream *stream);
>> +                               struct avdtp_remote_sep *rsep,
>> +                               avdtp_get_configuration_cb_t cb,
>> +                               void *user_data);
>>
>>   int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
>>   int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
>> --
>> 2.43.0
>>
>>
>

^ permalink raw reply

* Re: [PATCH BlueZ] a2dp: Add codec prioritization
From: Simon Mikuda @ 2026-06-09 16:18 UTC (permalink / raw)
  To: Pauli Virtanen, linux-bluetooth; +Cc: Luiz Augusto von Dentz
In-Reply-To: <f10de0ea2a69c48b40d66b79d4236b17f932c05c.camel@iki.fi>

Hi,

in our use case it was something like this:

we have endpoints:

AAC, SBC

later we've added LDAC and to ensure compatibility with already paired 
devices we had to add it to the end of the list:

AAC, SBC, LDAC

So when we've initiated connection to remote that supports LDAC, it was 
not selected.

But now i think your idea:

    What could be useful is something that allows per-device priority
    selection, e.g. DBus callback that BlueZ uses to query which endpoint
    to configure, before starting SelectConfiguration.

Is the way to go, definitely better than static priority in bluez.

On 6/8/26 17:58, Pauli Virtanen wrote:
> Hi,
>
> ma, 2026-06-08 kello 13:16 +0200, Simon Mikuda kirjoitti:
>> This change will select best codec when connecting to A2DP:
>> LDAC > AptX HD > AptX > AAC > SBC
> Currently it's media server's job to decide what A2DP codec to use.
>
> What they do for this sort of static ordering, is to register endpoints
> in the wanted priority order, so I think another mechanism for fixed
> ordering is not really needed.
>
> What could be useful is something that allows per-device priority
> selection, e.g. DBus callback that BlueZ uses to query which endpoint
> to configure, before starting SelectConfiguration.
>
> I'm not sure this is highly needed though, as iirc BlueZ remembers what
> configuration was used on previous connects, and if BlueZ gets it wrong
> the sound server can just reconfigure as needed at the cost of some
> extra delay when connecting.
>
>> ---
>>   profiles/audio/avdtp.c | 51 +++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 50 insertions(+), 1 deletion(-)
>>
>> diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
>> index d475a545e..a8733a7e1 100644
>> --- a/profiles/audio/avdtp.c
>> +++ b/profiles/audio/avdtp.c
>> @@ -42,6 +42,8 @@
>>   #include "sink.h"
>>   #include "source.h"
>>   
>> +#include "a2dp-codecs.h"
>> +
>>   #define AVDTP_PSM 25
>>   
>>   #define MAX_SEID 0x3E
>> @@ -1293,10 +1295,49 @@ static struct avdtp_stream *find_stream_by_lseid(struct avdtp *session,
>>   	return find_stream_by_lsep(session, sep);
>>   }
>>   
>> +static unsigned int get_codec_priority(
>> +				struct avdtp_media_codec_capability *codec_cap)
>> +{
>> +	unsigned int priority = codec_cap->media_codec_type;
>> +
>> +	if (codec_cap->media_codec_type == A2DP_CODEC_VENDOR) {
>> +		a2dp_vendor_codec_t *vendor_codec = (void *) codec_cap->data;
>> +
>> +		switch (A2DP_GET_VENDOR_ID(*vendor_codec)) {
>> +		case APTX_VENDOR_ID:
>> +			return priority + 100;
>> +		case APTX_HD_VENDOR_ID:
>> +			return priority + 200;
>> +		case LDAC_VENDOR_ID:
>> +			return priority + 300;
>> +		}
>> +	}
>> +
>> +	return priority;
>> +}
>> +
>> +static int sep_codec_cmp(gconstpointer a, gconstpointer b)
>> +{
>> +	const struct avdtp_remote_sep *sep1 = a;
>> +	struct avdtp_service_capability *cap1 = sep1->codec;
>> +	unsigned int priority1 = get_codec_priority((void *) cap1->data);
>> +
>> +	const struct avdtp_remote_sep *sep2 = b;
>> +	struct avdtp_service_capability *cap2 = sep2->codec;
>> +	unsigned int priority2 = get_codec_priority((void *) cap2->data);
>> +
>> +	if (priority1 < priority2)
>> +		return 1;
>> +	if (priority1 > priority2)
>> +		return -1;
>> +	return 0;
>> +}
>> +
>>   struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
>>   						struct avdtp_local_sep *lsep)
>>   {
>>   	GSList *l;
>> +	GSList *sorted = NULL;
>>   
>>   	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
>>   		struct avdtp_remote_sep *sep = l->data;
>> @@ -1325,7 +1366,15 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
>>   				continue;
>>   
>>   		if (sep->stream == NULL)
>> -			return sep;
>> +			sorted = g_slist_insert_sorted(sorted, sep,
>> +							sep_codec_cmp);
>> +	}
>> +
>> +	if (sorted) {
>> +		struct avdtp_remote_sep *sep = sorted->data;
>> +
>> +		g_slist_free(sorted);
>> +		return sep;
>>   	}
>>   
>>   	return NULL;

^ permalink raw reply

* Re: [PATCH BlueZ] hog: Fix starting encryption on some BLE remotes
From: Simon Mikuda @ 2026-06-09 16:25 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <CABBYNZLRz2iqR-eQNjXsJnyeZ8aONnjgaQDO6bRWVqfVy8SbbQ@mail.gmail.com>

Hi Luiz.
I don't think it forces encryption without bonding.
The diff is:
          if (!device_is_bonded(device, 
btd_device_get_bdaddr_type(device))) {
     ........
+        } else if (auto_sec) {
+  bt_gatt_client_set_security(btd_device_get_gatt_client(device),
+                                BT_ATT_SECURITY_MEDIUM);
          }

so it means device IS bonded and auto_sec is enabled


On 6/8/26 16:23, Luiz Augusto von Dentz wrote:
> Hi Simon,
>
> On Mon, Jun 8, 2026 at 5:20 AM Simon Mikuda
> <simon.mikuda@streamunlimited.com> wrote:
>> When BLE remote doesn't reply with Insufficient Authentication Error
>> encryption is not started.
>>
>> Good remote:
>> < ACL Data TX: Handle 64 flags 0x00 dlen 7
>>        ATT: Read Request (0x0a) len 2
>>          Handle: 0x0021 Type: HID Information (0x2a4a)
>>> ACL Data RX: Handle 64 flags 0x02 dlen 9
>>        ATT: Error Response (0x01) len 4
>>          Read Request (0x0a)
>>          Handle: 0x0000
>>          Error: Insufficient Authentication (0x05)
>> < HCI Command: LE Start Encryption (0x08|0x0019) plen 28
>>          Handle: 64 Address: xx:xx:xx:xx:xx:xx (OUI xx-xx-xx)
>>          Random number: ...
>>          Encrypted diversifier: ...
>>          Long term key: ...
>>
>> Bad remote:
>> < ACL Data TX: Handle 64 flags 0x00 dlen 7
>>        ATT: Read Request (0x0a) len 2
>>          Handle: 0x001e Type: HID Information (0x2a4a)
>>> ACL Data RX: Handle 64 flags 0x02 dlen 9
>>        ATT: Read Response (0x0b) len 4
>> ---
>>   profiles/input/hog.c | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/profiles/input/hog.c b/profiles/input/hog.c
>> index f50a0f217..845087c9d 100644
>> --- a/profiles/input/hog.c
>> +++ b/profiles/input/hog.c
>> @@ -189,6 +189,9 @@ static int hog_accept(struct btd_service *service)
>>                  if (!bt_gatt_client_set_security(client,
>>                                                  BT_ATT_SECURITY_MEDIUM))
>>                          return -ECONNREFUSED;
>> +       } else if (auto_sec) {
>> +               bt_gatt_client_set_security(btd_device_get_gatt_client(device),
>> +                               BT_ATT_SECURITY_MEDIUM);
> So this forces encryption even without bonding; why?
>
>>          }
>>
>>          /* TODO: Replace GAttrib with bt_gatt_client */
>> --
>> 2.43.0
>>
>>
>

^ permalink raw reply

* Re: [PATCH BlueZ] hog: Fix starting encryption on some BLE remotes
From: Luiz Augusto von Dentz @ 2026-06-09 16:35 UTC (permalink / raw)
  To: Simon Mikuda; +Cc: linux-bluetooth
In-Reply-To: <11d03be6-8f9c-4b8c-8bd7-0b7d9c9d87b4@streamunlimited.com>

Hi Simon,

On Tue, Jun 9, 2026 at 12:25 PM Simon Mikuda
<simon.mikuda@streamunlimited.com> wrote:
>
> Hi Luiz.
> I don't think it forces encryption without bonding.
> The diff is:
>           if (!device_is_bonded(device,
> btd_device_get_bdaddr_type(device))) {
>      ........
> +        } else if (auto_sec) {
> +  bt_gatt_client_set_security(btd_device_get_gatt_client(device),
> +                                BT_ATT_SECURITY_MEDIUM);
>           }
>
> so it means device IS bonded and auto_sec is enabled

Hmm, but in that case doesn't the following code trigger first:

https://github.com/bluez/bluez/blob/master/src/device.c#L6469

So either there is a bug or the code you are adding is just a NOP
because the security has already been set to BT_ATT_SECURITY_MEDIUM.

>
> On 6/8/26 16:23, Luiz Augusto von Dentz wrote:
> > Hi Simon,
> >
> > On Mon, Jun 8, 2026 at 5:20 AM Simon Mikuda
> > <simon.mikuda@streamunlimited.com> wrote:
> >> When BLE remote doesn't reply with Insufficient Authentication Error
> >> encryption is not started.
> >>
> >> Good remote:
> >> < ACL Data TX: Handle 64 flags 0x00 dlen 7
> >>        ATT: Read Request (0x0a) len 2
> >>          Handle: 0x0021 Type: HID Information (0x2a4a)
> >>> ACL Data RX: Handle 64 flags 0x02 dlen 9
> >>        ATT: Error Response (0x01) len 4
> >>          Read Request (0x0a)
> >>          Handle: 0x0000
> >>          Error: Insufficient Authentication (0x05)
> >> < HCI Command: LE Start Encryption (0x08|0x0019) plen 28
> >>          Handle: 64 Address: xx:xx:xx:xx:xx:xx (OUI xx-xx-xx)
> >>          Random number: ...
> >>          Encrypted diversifier: ...
> >>          Long term key: ...
> >>
> >> Bad remote:
> >> < ACL Data TX: Handle 64 flags 0x00 dlen 7
> >>        ATT: Read Request (0x0a) len 2
> >>          Handle: 0x001e Type: HID Information (0x2a4a)
> >>> ACL Data RX: Handle 64 flags 0x02 dlen 9
> >>        ATT: Read Response (0x0b) len 4
> >> ---
> >>   profiles/input/hog.c | 3 +++
> >>   1 file changed, 3 insertions(+)
> >>
> >> diff --git a/profiles/input/hog.c b/profiles/input/hog.c
> >> index f50a0f217..845087c9d 100644
> >> --- a/profiles/input/hog.c
> >> +++ b/profiles/input/hog.c
> >> @@ -189,6 +189,9 @@ static int hog_accept(struct btd_service *service)
> >>                  if (!bt_gatt_client_set_security(client,
> >>                                                  BT_ATT_SECURITY_MEDIUM))
> >>                          return -ECONNREFUSED;
> >> +       } else if (auto_sec) {
> >> +               bt_gatt_client_set_security(btd_device_get_gatt_client(device),
> >> +                               BT_ATT_SECURITY_MEDIUM);
> > So this forces encryption even without bonding; why?
> >
> >>          }
> >>
> >>          /* TODO: Replace GAttrib with bt_gatt_client */
> >> --
> >> 2.43.0
> >>
> >>
> >



-- 
Luiz Augusto von Dentz

^ permalink raw reply

* [bluez/bluez] 27eb49: shared/hci: Avoid redundant BPF filter updates on ...
From: raghava447 @ 2026-06-09 16:37 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/master
  Home:   https://github.com/bluez/bluez
  Commit: 27eb493882aed659339579adb29ca99b61042379
      https://github.com/bluez/bluez/commit/27eb493882aed659339579adb29ca99b61042379
  Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  Date:   2026-06-08 (Mon, 08 Jun 2026)

  Changed paths:
    M src/shared/hci.c

  Log Message:
  -----------
  shared/hci: Avoid redundant BPF filter updates on duplicate events

Skip updating the BPF socket filter in bt_hci_register and
bt_hci_register_subevent when the event/subevent is already
registered, since it is already part of the filter.

Similarly, skip updating the filter in bt_hci_unregister and
bt_hci_unregister_subevent when other handlers for the same
event/subevent still remain in the queue.

This avoids unnecessary setsockopt(SO_ATTACH_FILTER) calls when
multiple handlers are registered for the same event code.


  Commit: 9320e1cb47494aa67cf86bc0dda1913f4e489478
      https://github.com/bluez/bluez/commit/9320e1cb47494aa67cf86bc0dda1913f4e489478
  Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
  Date:   2026-06-08 (Mon, 08 Jun 2026)

  Changed paths:
    M src/shared/hci.c

  Log Message:
  -----------
  shared/hci: Debounce SO_ATTACH_FILTER with timeout_add(0)

Coalesce multiple BPF filter updates into a single SO_ATTACH_FILTER
setsockopt call by deferring the update to the next event loop
iteration using timeout_add(0, ...).

When bt_hci_register_event or bt_hci_register_subevent is called
multiple times in succession (e.g. from bt_rap_attach_hci), each call
previously triggered a full filter rebuild and setsockopt. Now,
schedule_evt_filter() simply marks a pending update which fires once
in filter_timeout() after all synchronous registrations complete.


  Commit: 8bc6dd81c9c21137f0b63ac59db49c26b6af6c65
      https://github.com/bluez/bluez/commit/8bc6dd81c9c21137f0b63ac59db49c26b6af6c65
  Author: raghu447 <raghavendra.rao@collabora.com>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M src/shared/bap.c

  Log Message:
  -----------
  shared/bap: add ASE Control Point error responses

These changes are required to Pass BAP/USR/SPE/BI-01[5]-C tests.


  Commit: 912d67d1d3a46f5ef946eec2dbe310b97247f085
      https://github.com/bluez/bluez/commit/912d67d1d3a46f5ef946eec2dbe310b97247f085
  Author: raghu447 <raghavendra.rao@collabora.com>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M unit/test-bap.c

  Log Message:
  -----------
  unit/bap: add SPE tests

Add unit tests for the PTS BAP/USR/SPE/BI-01[5]-C tests.


Compare: https://github.com/bluez/bluez/compare/622a46ebcd72...912d67d1d3a4

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez] 2fe40b: shared/util: Fix warnings when cleaning up NULL po...
From: hadess @ 2026-06-09 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1108616
  Home:   https://github.com/bluez/bluez
  Commit: 2fe40bee6c48c4d72506d98d3283b8d1e981a75d
      https://github.com/bluez/bluez/commit/2fe40bee6c48c4d72506d98d3283b8d1e981a75d
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M src/shared/util.h

  Log Message:
  -----------
  shared/util: Fix warnings when cleaning up NULL pointers

Don't try to free NULL pointers in cleanup callbacks, some of the
functions we use to free pointers might not support being passed
non-NULL values.

Fixes: 2e0533f977cc ("shared/util: Add helper for "cleanup" variable attribute")


  Commit: c58eb86c244e8fbdc32f9d58f395ad387ca67f09
      https://github.com/bluez/bluez/commit/c58eb86c244e8fbdc32f9d58f395ad387ca67f09
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M src/shared/util.h

  Log Message:
  -----------
  shared/util: Better type-checking in _steal_* commands

Replace _steal_() and _steal_fd_() inline functions with macros, so that
we can have better type-checking. This would have avoided crashes seen
in the original patchset, where we passed a pointer instead of a pointer
to a pointer.

Example usage:
CLEANUP_FREEFUNC(GKeyFile, g_key_file_free);
GKeyfile *load_config(void) {
  _cleanup_type_(GKeyFile) GKeyFile *keyfile = NULL;
  [...]
  return _steal_(keyfile);
}

static int open_file(void) {
  _cleanup_fd_ int fd = -1;
  [...]
  return _steal_fd_(fd);
}

This is inspired by similar code in Pipewire's spa helpers, as pointed
out by Pauli Virtanen <pav@iki.fi>.


  Commit: ab59047bfee892cae87896575ad65c8b6944e7da
      https://github.com/bluez/bluez/commit/ab59047bfee892cae87896575ad65c8b6944e7da
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M doc/maintainer-guidelines.rst

  Log Message:
  -----------
  doc: Recommend using _cleanup_ and friends


  Commit: b6f94e7d47b312ab1d373abf9a7bf1d7d32d50b4
      https://github.com/bluez/bluez/commit/b6f94e7d47b312ab1d373abf9a7bf1d7d32d50b4
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M src/main.c

  Log Message:
  -----------
  main: Use _cleanup_() to simplify configuration parsing

Use helpers to simplify temporary string usage, and cleanup in error
paths.


  Commit: 09fbfb78eb0834cdbe9f7353b186d481b17321f6
      https://github.com/bluez/bluez/commit/09fbfb78eb0834cdbe9f7353b186d481b17321f6
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M client/mgmt.c

  Log Message:
  -----------
  client: Use _cleanup_fd_ to simplify urandom access

fd gets auto-closed before exiting the scope.


  Commit: e22bd838f275c5144501bbbf961b91a28871c9e0
      https://github.com/bluez/bluez/commit/e22bd838f275c5144501bbbf961b91a28871c9e0
  Author: Bastien Nocera <hadess@hadess.net>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M tools/btattach.c

  Log Message:
  -----------
  btattach: Use _cleanup_fd_ to simplify error paths

Use _cleanup_fd_ and _steal_fd() to simplify error paths, and only
"steal" the file descriptor on success.


Compare: https://github.com/bluez/bluez/compare/2fe40bee6c48%5E...e22bd838f275

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez] 87d07a: shared: rap: Check role before sending CS Sec Enab...
From: Bhavani @ 2026-06-09 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1108690
  Home:   https://github.com/bluez/bluez
  Commit: 87d07a7981815712c9d8bd855c25b038ec88c2c1
      https://github.com/bluez/bluez/commit/87d07a7981815712c9d8bd855c25b038ec88c2c1
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M src/shared/rap.h

  Log Message:
  -----------
  shared: rap: Check role before sending CS Sec Enable cmd

Add the is_central parameter to verify whether
the local role is central before sending
the HCI CS Security Enable command.


  Commit: 8c2ee0c8aa1082570e31c7a3321b7a304453b86a
      https://github.com/bluez/bluez/commit/8c2ee0c8aa1082570e31c7a3321b7a304453b86a
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-06-09 (Tue, 09 Jun 2026)

  Changed paths:
    M profiles/ranging/rap.c
    M profiles/ranging/rap_hci.c

  Log Message:
  -----------
  profiles: ranging: Add CS Initiator cmd and evt handling

Introduce support for LE Channel Sounding (CS)
ranging procedures in the Initiator role by enabling
required HCI command sequencing and event handling.

Add handling of core HCI LE CS commands and events
This enables cs capability discovery, cs configuration
management and execution of CS ranging procedures
in the Initiator role.


Compare: https://github.com/bluez/bluez/compare/87d07a798181%5E...8c2ee0c8aa10

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-06-09 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1108507
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-06-09 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1108483
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-06-09 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1108111
  Home:   https://github.com/bluez/bluez

To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications

^ permalink raw reply

* Re: [PATCH v1] Bluetooth: btintel_pcie: Load IOSF debug regs by controller variant
From: patchwork-bot+bluetooth @ 2026-06-09 16:40 UTC (permalink / raw)
  To: Kiran K
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
	chandrashekar.devegowda, aluvala.sai.teja
In-Reply-To: <20260607062117.110893-1-kiran.k@intel.com>

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Sun,  7 Jun 2026 11:51:17 +0530 you wrote:
> From: Sai Teja Aluvala <aluvala.sai.teja@intel.com>
> 
> Load the IOSF DBGC base address based on the controller hardware
> variant when reading DRAM buffers during a trace dump. Scorpius
> Peak family controllers (SCP/SCP2/SCP2F) use a different DBGC base
> address (0xf0d5d500) than Blazar family controllers (BZRI/BZRIW,
> 0xf3800300).
> 
> [...]

Here is the summary with links:
  - [v1] Bluetooth: btintel_pcie: Load IOSF debug regs by controller variant
    https://git.kernel.org/bluetooth/bluetooth-next/c/e5ea095d9bd1

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v1] Bluetooth: btintel_pcie: Add 50 ms delay before MAC init on BlazarIW
From: patchwork-bot+bluetooth @ 2026-06-09 16:40 UTC (permalink / raw)
  To: Kiran K
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
	chandrashekar.devegowda
In-Reply-To: <20260606003637.94628-1-kiran.k@intel.com>

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Sat,  6 Jun 2026 06:06:37 +0530 you wrote:
> On BlazarIW, fast restart cycles fail because the D0 entry to MAC
> init does not complete in time. As a result, MAC initialization
> does not proceed and the controller fails to transition past the
> ROM boot stage.
> 
> Add a 50 ms delay (worst case as per HW analysis) before doing MAC
> init in btintel_pcie_enable_bt() so the shared hardware reset flow
> has time to complete. The delay is gated on the BlazarIW PCI device
> id 0x4D76 so other Intel BT PCIe controllers are unaffected.
> 
> [...]

Here is the summary with links:
  - [v1] Bluetooth: btintel_pcie: Add 50 ms delay before MAC init on BlazarIW
    https://git.kernel.org/bluetooth/bluetooth-next/c/202af099928b

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH BlueZ] hog: Fix starting encryption on some BLE remotes
From: Simon Mikuda @ 2026-06-09 16:46 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <CABBYNZLomccRxaXOMDxbJ2ksks-2C30h9+oSn1ZRn1cKohThfA@mail.gmail.com>

Ahh. We have this modified in our codebase - we have a change that you 
will not increase security yourself you leave it to remote device (in 
case user has removed LTK on remote device and you still have one, you 
will not trigger authentication error).

So you're right, if this is in place it would have increase the 
security. We will squash those commits together internally.

Thanks!

On 6/9/26 18:35, Luiz Augusto von Dentz wrote:
> Hi Simon,
>
> On Tue, Jun 9, 2026 at 12:25 PM Simon Mikuda
> <simon.mikuda@streamunlimited.com> wrote:
>> Hi Luiz.
>> I don't think it forces encryption without bonding.
>> The diff is:
>>            if (!device_is_bonded(device,
>> btd_device_get_bdaddr_type(device))) {
>>       ........
>> +        } else if (auto_sec) {
>> +  bt_gatt_client_set_security(btd_device_get_gatt_client(device),
>> +                                BT_ATT_SECURITY_MEDIUM);
>>            }
>>
>> so it means device IS bonded and auto_sec is enabled
> Hmm, but in that case doesn't the following code trigger first:
>
> https://github.com/bluez/bluez/blob/master/src/device.c#L6469
>
> So either there is a bug or the code you are adding is just a NOP
> because the security has already been set to BT_ATT_SECURITY_MEDIUM.
>
>> On 6/8/26 16:23, Luiz Augusto von Dentz wrote:
>>> Hi Simon,
>>>
>>> On Mon, Jun 8, 2026 at 5:20 AM Simon Mikuda
>>> <simon.mikuda@streamunlimited.com> wrote:
>>>> When BLE remote doesn't reply with Insufficient Authentication Error
>>>> encryption is not started.
>>>>
>>>> Good remote:
>>>> < ACL Data TX: Handle 64 flags 0x00 dlen 7
>>>>         ATT: Read Request (0x0a) len 2
>>>>           Handle: 0x0021 Type: HID Information (0x2a4a)
>>>>> ACL Data RX: Handle 64 flags 0x02 dlen 9
>>>>         ATT: Error Response (0x01) len 4
>>>>           Read Request (0x0a)
>>>>           Handle: 0x0000
>>>>           Error: Insufficient Authentication (0x05)
>>>> < HCI Command: LE Start Encryption (0x08|0x0019) plen 28
>>>>           Handle: 64 Address: xx:xx:xx:xx:xx:xx (OUI xx-xx-xx)
>>>>           Random number: ...
>>>>           Encrypted diversifier: ...
>>>>           Long term key: ...
>>>>
>>>> Bad remote:
>>>> < ACL Data TX: Handle 64 flags 0x00 dlen 7
>>>>         ATT: Read Request (0x0a) len 2
>>>>           Handle: 0x001e Type: HID Information (0x2a4a)
>>>>> ACL Data RX: Handle 64 flags 0x02 dlen 9
>>>>         ATT: Read Response (0x0b) len 4
>>>> ---
>>>>    profiles/input/hog.c | 3 +++
>>>>    1 file changed, 3 insertions(+)
>>>>
>>>> diff --git a/profiles/input/hog.c b/profiles/input/hog.c
>>>> index f50a0f217..845087c9d 100644
>>>> --- a/profiles/input/hog.c
>>>> +++ b/profiles/input/hog.c
>>>> @@ -189,6 +189,9 @@ static int hog_accept(struct btd_service *service)
>>>>                   if (!bt_gatt_client_set_security(client,
>>>>                                                   BT_ATT_SECURITY_MEDIUM))
>>>>                           return -ECONNREFUSED;
>>>> +       } else if (auto_sec) {
>>>> +               bt_gatt_client_set_security(btd_device_get_gatt_client(device),
>>>> +                               BT_ATT_SECURITY_MEDIUM);
>>> So this forces encryption even without bonding; why?
>>>
>>>>           }
>>>>
>>>>           /* TODO: Replace GAttrib with bt_gatt_client */
>>>> --
>>>> 2.43.0
>>>>
>>>>
>
>

^ permalink raw reply

* [PATCH BlueZ v1 1/4] btio: Handle EOPNOTSUPP from accept() to prevent busy loop
From: Luiz Augusto von Dentz @ 2026-06-09 16:50 UTC (permalink / raw)
  To: linux-bluetooth

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

When accept() returns EOPNOTSUPP on an L2CAP SEQPACKET server socket
(e.g. AVCTP browsing channel, PSM 0x1b), the error is permanent and
retrying will never succeed. Previously, only EBADFD was treated as
fatal, causing server_cb to return TRUE for EOPNOTSUPP. Since the fd
remains readable, this creates an infinite busy loop that hangs
bluetoothd.

Treat EOPNOTSUPP the same as EBADFD by returning FALSE to remove the
GLib IO watch and stop the loop.
---
 btio/btio.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/btio/btio.c b/btio/btio.c
index cfaa9392de12..8d71a210d505 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -254,7 +254,7 @@ static gboolean server_cb(GIOChannel *io, GIOCondition cond,
 
 	cli_sock = accept(srv_sock, NULL, NULL);
 	if (cli_sock < 0) {
-		if (errno == EBADFD)
+		if (errno == EBADFD || errno == EOPNOTSUPP)
 			return FALSE;
 		return TRUE;
 	}
-- 
2.54.0


^ permalink raw reply related

* [PATCH BlueZ v1 2/4] profile: Check if bearer is enabled on registration
From: Luiz Augusto von Dentz @ 2026-06-09 16:50 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260609165057.90837-1-luiz.dentz@gmail.com>

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

btd_profile_register now verifies that the profile's bearer type is
compatible with btd_opts.mode before registering. If the required bearer
is not enabled (e.g. LE-only profile when mode is BR/EDR, or BR/EDR-only
profile when mode is LE), registration is rejected with -ENOTSUP.
---
 src/profile.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/profile.c b/src/profile.c
index 65df0f7a0969..97fffe9b4d5c 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -36,6 +36,7 @@
 #include "dbus-common.h"
 #include "sdp-client.h"
 #include "sdp-xml.h"
+#include "btd.h"
 #include "adapter.h"
 #include "device.h"
 #include "profile.h"
@@ -802,6 +803,14 @@ struct btd_profile *btd_profile_find_remote_uuid(const char *uuid)
 
 int btd_profile_register(struct btd_profile *profile)
 {
+	if ((profile->bearer == BTD_PROFILE_BEARER_LE &&
+				btd_opts.mode == BT_MODE_BREDR) ||
+			(profile->bearer == BTD_PROFILE_BEARER_BREDR &&
+				btd_opts.mode == BT_MODE_LE)) {
+		DBG("Bearer not enabled");
+		return -ENOTSUP;
+	}
+
 	if (profile->experimental && !(g_dbus_get_flags() &
 					G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
 		DBG("D-Bus experimental not enabled");
-- 
2.54.0


^ permalink raw reply related


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