Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v4] Bluetooth: bnep: reject short frames before parsing
From: Zhang Cen @ 2026-05-21  5:26 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-kernel, zerocling0077, 2045gemini,
	Zhang Cen

A BNEP peer can send a short BNEP SDU. bnep_rx_frame() reads the
packet type byte immediately and, for control packets, reads the control
opcode and setup UUID-size byte before proving that those bytes are
present. bnep_rx_control() also dereferences the control opcode without
rejecting an empty control payload.

Use skb_pull_data() for the fixed fields in bnep_rx_frame() so a NULL
return gates each dereference. Split the control handler so the frame
path can pass an opcode that has already been pulled, and keep the
byte-buffer wrapper for extension control payloads.

Validation reproduced this kernel report:
KASAN slab-out-of-bounds in bnep_rx_frame.isra.0+0x130c/0x1790
The buggy address belongs to the object at ffff88800c0f7908 which belongs
to the cache kmalloc-8 of size 8
The buggy address is located 0 bytes to the right of allocated 1-byte
region [ffff88800c0f7908, ffff88800c0f7909)
Read of size 1
Call trace:
  dump_stack_lvl+0xb3/0x140 (?:?)
  print_address_description+0x57/0x3a0 (?:?)
  bnep_rx_frame+0x130c/0x1790 (net/bluetooth/bnep/core.c:306)
  print_report+0xb9/0x2b0 (?:?)
  __virt_addr_valid+0x1ba/0x3a0 (?:?)
  srso_alias_return_thunk+0x5/0xfbef5 (?:?)
  kasan_addr_to_slab+0x21/0x60 (?:?)
  kasan_report+0xe0/0x110 (?:?)
  process_one_work+0xfce/0x17e0 (kernel/workqueue.c:3200)
  worker_thread+0x65c/0xe40 (?:?)
  __kthread_parkme+0x184/0x230 (?:?)
  kthread+0x35e/0x470 (?:?)
  _raw_spin_unlock_irq+0x28/0x50 (?:?)
  ret_from_fork+0x586/0x870 (?:?)
  __switch_to+0x74f/0xdc0 (?:?)
  ret_from_fork_asm+0x1a/0x30 (?:?)

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
---
 net/bluetooth/bnep/core.c | 51 ++++++++++++++++++++++++---------------
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 853c8d764..156cd3026 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -206,14 +206,11 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
 	return 0;
 }
 
-static int bnep_rx_control(struct bnep_session *s, void *data, int len)
+static int bnep_rx_control_cmd(struct bnep_session *s, u8 cmd, void *data,
+			       int len)
 {
-	u8  cmd = *(u8 *)data;
 	int err = 0;
 
-	data++;
-	len--;
-
 	switch (cmd) {
 	case BNEP_CMD_NOT_UNDERSTOOD:
 	case BNEP_SETUP_CONN_RSP:
@@ -254,6 +251,14 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
 	return err;
 }
 
+static int bnep_rx_control(struct bnep_session *s, void *data, int len)
+{
+	if (len < 1)
+		return -EILSEQ;
+
+	return bnep_rx_control_cmd(s, *(u8 *)data, data + 1, len - 1);
+}
+
 static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
 {
 	struct bnep_ext_hdr *h;
@@ -299,19 +304,26 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
 {
 	struct net_device *dev = s->dev;
 	struct sk_buff *nskb;
+	u8 *data;
 	u8 type, ctrl_type;
 
 	dev->stats.rx_bytes += skb->len;
 
-	type = *(u8 *) skb->data;
-	skb_pull(skb, 1);
-	ctrl_type = *(u8 *)skb->data;
+	data = skb_pull_data(skb, sizeof(type));
+	if (!data)
+		goto badframe;
+	type = *data;
 
 	if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
 		goto badframe;
 
 	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
-		if (bnep_rx_control(s, skb->data, skb->len) < 0) {
+		data = skb_pull_data(skb, sizeof(ctrl_type));
+		if (!data)
+			goto badframe;
+		ctrl_type = *data;
+
+		if (bnep_rx_control_cmd(s, ctrl_type, skb->data, skb->len) < 0) {
 			dev->stats.tx_errors++;
 			kfree_skb(skb);
 			return 0;
@@ -325,23 +337,22 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
 		/* Verify and pull ctrl message since it's already processed */
 		switch (ctrl_type) {
 		case BNEP_SETUP_CONN_REQ:
-			/* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
-			if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
+			/* Pull: len (1 b), data (len * 2 bytes) */
+			data = skb_pull_data(skb, 1);
+			if (!data)
+				goto badframe;
+			if (!skb_pull(skb, *data * 2))
 				goto badframe;
 			break;
 		case BNEP_FILTER_MULTI_ADDR_SET:
-		case BNEP_FILTER_NET_TYPE_SET: {
-			u8 *hdr;
-
-			/* Pull ctrl type (1 b) + len (2 b) */
-			hdr = skb_pull_data(skb, 3);
-			if (!hdr)
+		case BNEP_FILTER_NET_TYPE_SET:
+			/* Pull: len (2 b), data (len bytes) */
+			data = skb_pull_data(skb, sizeof(u16));
+			if (!data)
 				goto badframe;
-			/* Pull data (len bytes); length is big-endian */
-			if (!skb_pull(skb, get_unaligned_be16(&hdr[1])))
+			if (!skb_pull(skb, get_unaligned_be16(data)))
 				goto badframe;
 			break;
-		}
 		default:
 			kfree_skb(skb);
 			return 0;
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v2] Bluetooth: btusb: Allow firmware re-download when version matches
From: Shuai Zhang @ 2026-05-21  5:35 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, linux-arm-msm,
	cheng.jiang, quic_chezhou, wei.deng, jinwang.li, mengshi.wu
In-Reply-To: <CABBYNZJpW8=KtN9_G2QOp1YqD3ZJhWGVriyLBh7jUGRFEHPyYg@mail.gmail.com>

Hi Luiz

On 5/20/2026 8:45 PM, Luiz Augusto von Dentz wrote:
> Hi Shuai,
>
> On Wed, May 20, 2026 at 2:26 AM Shuai Zhang
> <shuai.zhang@oss.qualcomm.com> wrote:
>> Hi Luiz
>>
>> On 4/29/2026 11:17 PM, Luiz Augusto von Dentz wrote:
>>> Hi Shuai,
>>>
>>> On Wed, Apr 29, 2026 at 8:12 AM Shuai Zhang
>>> <shuai.zhang@oss.qualcomm.com> wrote:
>>>> The Bluetooth host decides whether to download firmware by reading the
>>>> controller firmware download completion flag and firmware version
>>>> information.
>>>>
>>>> If a USB error occurs during the firmware download process (for example
>>>> due to a USB disconnect), the download is aborted immediately. An
>>>> incomplete firmware transfer does not cause the controller to set the
>>>> download completion flag, but the firmware version information may be
>>>> updated at an early stage of the download process.
>>> Hold on, if the download has been aborted then the version should be
>>> reverted, or rather just update once the firmware loading is complete,
>>> so this indicates there is a bug somewhere that needs fixing, not
>>> worked around.
>>>
>>>> In this case, after USB reconnection, the host attempts to re-download
>>>> the firmware because the download completion flag is not set. However,
>>>> since the controller reports the same firmware version as the target
>>>> firmware, the download is skipped. This ultimately results in the
>>>> firmware not being properly updated on the controller.
>>>>
>>>> This change removes the restriction that skips firmware download when
>>>> the versions are equal. It covers scenarios where the USB connection
>>>> can be disconnected at any time and ensures that firmware download can
>>>> be retriggered after USB reconnection, allowing the Bluetooth firmware
>>>> to be correctly and completely updated.
>>>>
>>>> Signed-off-by: Shuai Zhang <shuai.zhang@oss.qualcomm.com>
>>>> ---
>>>> Changes v2:
>>>> - Update code comments and commit message to reflect the correct logic.
>>>> - Align the commit title with upstream conventions.
>>>> - Link v1
>>>>     https://lore.kernel.org/all/20260108074353.1027877-1-shuai.zhang@oss.qualcomm.com/
>>>> ---
>>>>    drivers/bluetooth/btusb.c | 8 +++++++-
>>>>    1 file changed, 7 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
>>>> index 572091e60..70abbabea 100644
>>>> --- a/drivers/bluetooth/btusb.c
>>>> +++ b/drivers/bluetooth/btusb.c
>>>> @@ -3550,7 +3550,13 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
>>>>                       "firmware rome 0x%x build 0x%x",
>>>>                       rver_rom, rver_patch, ver_rom, ver_patch);
>>>>
>>>> -       if (rver_rom != ver_rom || rver_patch <= ver_patch) {
>>>> +       /* Allow rampatch when the patch version equals the firmware version.
>>>> +        * A firmware download may be aborted by a transient USB error (e.g.
>>>> +        * disconnect) after the controller updates version info but before
>>>> +        * completion.
>>>> +        * Allowing equal versions enables re-flashing during recovery.
>>>> +        */
>>>> +       if (rver_rom != ver_rom || rver_patch < ver_patch) {
>>> As I said above, this sounds more like a workaround. That said, I
>>> wonder why it would print an error if the version matches, it sounds
>>> to be that if the version matches it should just skip and consider it
>>> has been loaded already in case the actual problem is fixed by setting
>>> the new version only when loading has been completed.

 From my understanding, the current changes should already cover this case,

so no further modifications seem necessary. Please let me know if you 
see it differently.


  I will add a Fixes tag in the next revision.


>>>>                   bt_dev_err(hdev, "rampatch file version did not match with firmware");
>>>>                   err = -EINVAL;
>>>>                   goto done;
>>>> --
>>>> 2.34.1
>> Just checking if there are any updates on this
> I had the impression there would be more changes needed, if this not
> the case than let me know, also perhaps we should consider adding a
> Fixes tag since this might help users experiencing problem if they are
> dual booting or somehow got the wrong firmware to be loaded.
>
>> Thanks,
>>
>> Shuai
>>

Thanks,

Shuai

>
>

^ permalink raw reply

* [bluez/bluez] 35baf7: shared/bap: Fix unused value in qos
From: michael kong @ 2026-05-21  5:50 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098419
  Home:   https://github.com/bluez/bluez
  Commit: 35baf761c1f830861a6bcacf856c4c95029148f9
      https://github.com/bluez/bluez/commit/35baf761c1f830861a6bcacf856c4c95029148f9
  Author: michael_kong <kx960506@163.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M src/shared/bap.c

  Log Message:
  -----------
  shared/bap: Fix unused value in qos

In bt_bap_parse_base, PresentationDelay is parsed but not written to QoS.


  Commit: e58872a259ad0973107ef479b33a4e2be273bbf6
      https://github.com/bluez/bluez/commit/e58872a259ad0973107ef479b33a4e2be273bbf6
  Author: michael_kong <kx960506@163.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M profiles/audio/transport.c

  Log Message:
  -----------
  transport: Add support for obtaining PresentationDelay in broadcast QoS


Compare: https://github.com/bluez/bluez/compare/35baf761c1f8%5E...e58872a259ad

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

^ permalink raw reply

* [PATCH BlueZ] doc: Fix Data Path direction in btmin-le-audio.rst
From: michael_kong @ 2026-05-21  6:09 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: michael_kong

The Data Path direction 0x01 should be Output (Controller to Host).
---
 doc/btmon-le-audio.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/btmon-le-audio.rst b/doc/btmon-le-audio.rst
index 50ee11879..47213c92e 100644
--- a/doc/btmon-le-audio.rst
+++ b/doc/btmon-le-audio.rst
@@ -454,12 +454,12 @@ On success, the controller assigns BIS connection handles (0x0010,
 
     < HCI Command: LE Setup ISO Data Path (0x08|0x006e) plen 13  #140 [hci0] 0.500003
           Connection Handle: 0x0010
-          Data Path Direction: Output (Host to Controller) (0x01)
+          Data Path Direction: Output (Controller to Host) (0x01)
           Data Path ID: HCI (0x00)
 
     < HCI Command: LE Setup ISO Data Path (0x08|0x006e) plen 13  #145 [hci0] 0.550003
           Connection Handle: 0x0011
-          Data Path Direction: Output (Host to Controller) (0x01)
+          Data Path Direction: Output (Controller to Host) (0x01)
           Data Path ID: HCI (0x00)
 
 **Step 8 -- ISO Data flows** on BIS handles::
-- 
2.29.1.windows.1


^ permalink raw reply related

* RE: [BlueZ,1/2] shared/bap: Fix unused value in qos
From: bluez.test.bot @ 2026-05-21  6:44 UTC (permalink / raw)
  To: linux-bluetooth, kx960506
In-Reply-To: <20260521032815.1845-1-kx960506@163.com>

[-- Attachment #1: Type: text/plain, Size: 2591 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=1098419

---Test result---

Test Summary:
CheckPatch                    PASS      0.66 seconds
GitLint                       FAIL      0.50 seconds
BuildEll                      PASS      18.04 seconds
BluezMake                     PASS      643.58 seconds
MakeCheck                     PASS      14.71 seconds
MakeDistcheck                 FAIL      213.39 seconds
CheckValgrind                 PASS      258.46 seconds
CheckSmatch                   WARNING   313.93 seconds
bluezmakeextell               PASS      169.78 seconds
IncrementalBuild              PASS      643.36 seconds
ScanBuild                     PASS      945.89 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,2/2] transport: Add support for obtaining PresentationDelay in broadcast QoS

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
1: T1 Title exceeds max length (83>80): "[BlueZ,2/2] transport: Add support for obtaining PresentationDelay in broadcast QoS"
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

make[4]: *** [Makefile:10239: test-suite.log] Error 1
make[3]: *** [Makefile:10347: check-TESTS] Error 2
make[2]: *** [Makefile:10818: check-am] Error 2
make[1]: *** [Makefile:10820: check] Error 2
make: *** [Makefile:10741: distcheck] Error 1
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structuressrc/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structuressrc/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structures


https://github.com/bluez/bluez/pull/2139

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH BlueZ v2] shared/rap: Add client ranging registration and notification parsing
From: Prathibha Madugonde @ 2026-05-21  7:18 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper fuctions for readablitiy and code indentation.

---
 src/shared/rap.c | 836 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 834 insertions(+), 2 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index 145da2060..3639c3f4d 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -29,6 +29,9 @@
 #define DBG(_rap, fmt, ...) \
 	rap_debug(_rap, "%s:%s() " fmt, __FILE__, __func__, ##__VA_ARGS__)
 
+#define SIGN_EXTEND_TO_16(val, bits) \
+	((int16_t)(((val) ^ (1U << ((bits)-1))) - (1U << ((bits)-1))))
+
 #define RAS_UUID16			0x185B
 
 /* Total number of attribute handles reserved for the RAS service */
@@ -43,6 +46,11 @@
 #define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
 #define RAS_SUBEVENT_HEADER_SIZE 8
 
+#define CS_MODE_ZERO_WIRE_INIT_SIZE 7
+#define CS_MODE_ZERO_WIRE_REF_SIZE 3
+#define CS_MODE_ONE_WIRE_SIZE_MIN 6
+#define CS_MODE_ONE_WIRE_SIZE_MAX 12
+
 enum pct_format {
 	IQ = 0,
 	PHASE = 1,
@@ -134,6 +142,28 @@ static inline void ranging_header_set_pct_format(struct ranging_header *hdr,
 				((format & 0x03) << 6);
 }
 
+static inline uint8_t ranging_header_get_antenna_mask(
+					const struct ranging_header *hdr)
+{
+	if (!hdr)
+		return 0;
+
+	return hdr->antenna_pct & 0x0F;
+}
+
+static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
+{
+	uint8_t count = 0;
+	uint8_t i;
+
+	for (i = 0; i < 4; i++) {
+		if (antenna_mask & (1u << i))
+			count++;
+	}
+
+	return count;
+}
+
 struct ras_subevent_header {
 	uint16_t start_acl_conn_event;
 	uint16_t frequency_compensation;
@@ -149,9 +179,21 @@ struct ras_subevent_header {
 #define RAS_DONE_STATUS_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_DONE_STATUS_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_DONE_STATUS_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 #define RAS_ABORT_REASON_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_ABORT_REASON_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_ABORT_REASON_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 struct ras_subevent {
 	struct ras_subevent_header subevent_header;
 	uint8_t subevent_data[];
@@ -206,6 +248,11 @@ struct cstracker {
 	uint16_t last_start_acl_conn_evt_counter;
 	uint16_t last_freq_comp;
 	int8_t last_ref_pwr_lvl;
+
+	/* Client - first segment carries this */
+	struct ranging_header   ranging_header_;
+	/* Client - subsequent segments appended using iovec */
+	struct iovec segment_data;
 };
 
 /* Ranging Service context */
@@ -254,6 +301,7 @@ struct bt_rap {
 	void *debug_data;
 	void *user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 };
 
 static struct queue *rap_db;
@@ -274,6 +322,28 @@ struct bt_rap_ready {
 	void *data;
 };
 
+typedef void (*rap_notify_t)(struct bt_rap *rap, uint16_t value_handle,
+			const uint8_t *value, uint16_t length,
+			void *user_data);
+
+struct bt_rap_notify {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_notify_t func;
+	void *user_data;
+};
+
+typedef void (*rap_func_t)(struct bt_rap *rap, bool success,
+			uint8_t att_ecode, const uint8_t *value,
+			uint16_t length, void *user_data);
+
+struct bt_rap_pending {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_func_t func;
+	void *userdata;
+};
+
 uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
 uint8_t ras_segment_header_size = 1;
 
@@ -542,6 +612,11 @@ static void rap_free(void *data)
 		rap->resptracker = NULL;
 	}
 
+	if (rap->reqtracker) {
+		free(rap->reqtracker);
+		rap->reqtracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -641,6 +716,12 @@ static void cs_tracker_init(struct cstracker *t)
 	t->last_start_acl_conn_evt_counter = 0;
 	t->last_freq_comp = 0;
 	t->last_ref_pwr_lvl = 0;
+
+	/* Initialize ranging header using helper functions */
+	memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
+	/* Initialize segment accumulator */
+	t->segment_data.iov_base = NULL;
+	t->segment_data.iov_len = 0;
 }
 
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
@@ -1698,6 +1779,42 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
 		cont->step_data);
 }
 
+static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result_cont *cont,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+}
+
+static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+
+}
+
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
@@ -1707,7 +1824,12 @@ void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
 
-	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result_cont(rap, cont,
+								length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
@@ -1720,7 +1842,11 @@ void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 	DBG(rap, "Received CS subevent: len=%d", length);
 
 	/* Populate CsProcedureData and send RAS payload */
-	form_ras_data_with_cs_subevent_result(rap, data, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result(rap, data, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
@@ -1763,6 +1889,7 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 
 	if (!rap)
 		return;
@@ -1781,6 +1908,19 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	resptracker->config_id = data->config_id;
 	resptracker->role = data->role;
 	resptracker->rtt_type = data->rtt_type;
+
+	if (!rap->reqtracker) {
+		reqtracker = new0(struct cstracker, 1);
+		cs_tracker_init(reqtracker);
+		rap->reqtracker = reqtracker;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	/* Basic fields */
+	reqtracker->config_id = data->config_id;
+	reqtracker->role = data->role;
+	reqtracker->rtt_type = data->rtt_type;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -1815,6 +1955,694 @@ done:
 	return rap;
 }
 
+static void ras_pending_destroy(void *data)
+{
+	struct bt_rap_pending *pending = data;
+	struct bt_rap *rap = pending->rap;
+
+	if (queue_remove_if(rap->pending, NULL, pending))
+		free(pending);
+}
+
+static void ras_pending_complete(bool success, uint8_t att_ecode,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct bt_rap_pending *pending = user_data;
+
+	if (pending->func)
+		pending->func(pending->rap, success, att_ecode, value, length,
+					  pending->userdata);
+}
+
+static void rap_read_value(struct bt_rap *rap, uint16_t value_handle,
+			   rap_func_t func, void *user_data)
+{
+	struct bt_rap_pending *pending;
+
+	pending = new0(struct bt_rap_pending, 1);
+	pending->rap = rap;
+	pending->func = func;
+	pending->userdata = user_data;
+
+	pending->id = bt_gatt_client_read_value(rap->client, value_handle,
+						ras_pending_complete, pending,
+						ras_pending_destroy);
+	if (!pending->id) {
+		DBG(rap, "Unable to send Read request");
+		free(pending);
+		return;
+	}
+
+	queue_push_tail(rap->pending, pending);
+}
+
+static void ras_register(uint16_t att_ecode, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (att_ecode)
+		DBG(notify->rap, "RAS register failed 0x%04x", att_ecode);
+
+	(void)notify;
+}
+
+static void rap_notify(uint16_t value_handle, const uint8_t *value,
+				uint16_t length, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (notify->func)
+		notify->func(notify->rap, value_handle, value, length,
+					 notify->user_data);
+}
+
+static void rap_notify_destroy(void *data)
+{
+	struct bt_rap_notify *notify = data;
+	struct bt_rap *rap = notify->rap;
+
+	if (queue_remove_if(rap->notify, NULL, notify))
+		free(notify);
+}
+
+static unsigned int bt_rap_register_notify(struct bt_rap *rap,
+					uint16_t value_handle,
+					rap_notify_t func,
+					void *user_data)
+{
+	struct bt_rap_notify *notify;
+
+	notify = new0(struct bt_rap_notify, 1);
+	notify->rap = rap;
+	notify->func = func;
+	notify->user_data = user_data;
+
+	DBG(rap, "register for notifications");
+
+	notify->id = bt_gatt_client_register_notify(rap->client,
+					value_handle, ras_register,
+					rap_notify, notify,
+					rap_notify_destroy);
+	if (!notify->id) {
+		DBG(rap, "Unable to register for notifications");
+		free(notify);
+		return 0;
+	}
+
+	queue_push_tail(rap->notify, notify);
+
+	return notify->id;
+}
+
+static inline bool parse_segmentation_header(struct iovec *iov,
+					     struct segmentation_header *s)
+{
+	uint8_t byte;
+
+	if (!util_iov_pull_u8(iov, &byte))
+		return false;
+
+	s->first_segment = (byte & 0x01) ? 1 : 0;
+	s->last_segment = (byte & 0x02) ? 1 : 0;
+	s->rolling_segment_counter = (byte >> 2) & 0x3F;
+
+	return true;
+}
+
+static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
+				int16_t *q_sample)
+{
+	uint32_t buffer;
+	uint32_t i12;
+	uint32_t q12;
+
+	if (!util_iov_pull_le24(iov, &buffer)) {
+		*i_sample = 0;
+		*q_sample = 0;
+		return;
+	}
+
+	i12 =  buffer        & 0x0FFFU;   /* bits 0..11 */
+	q12 = (buffer >> 12) & 0x0FFFU;   /* bits 12..23 */
+
+	*i_sample = SIGN_EXTEND_TO_16(i12, 12);
+	*q_sample = SIGN_EXTEND_TO_16(q12, 12);
+}
+
+static size_t get_mode_zero_length(enum cs_role remote_role)
+{
+	return (remote_role == CS_ROLE_INITIATOR) ?
+		CS_MODE_ZERO_WIRE_INIT_SIZE :
+		CS_MODE_ZERO_WIRE_REF_SIZE;
+}
+
+static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
+			    enum cs_role remote_role)
+{
+	uint8_t packet_quality;
+	int8_t packet_rssi_dbm;
+	uint8_t packet_ant;
+	uint32_t init_measured_freq_offset = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 0: failed to parse common fields");
+		return;
+	}
+
+	if (remote_role == CS_ROLE_INITIATOR) {
+		if (!util_iov_pull_le32(mode_iov, &init_measured_freq_offset)) {
+			DBG(rap, "Mode 0: failed to parse freq offset");
+			return;
+		}
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_one_length(bool include_pct)
+{
+	if (include_pct)
+		return CS_MODE_ONE_WIRE_SIZE_MAX;
+	return CS_MODE_ONE_WIRE_SIZE_MIN;
+}
+
+static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
+			   enum cs_role remote_role, bool include_pct)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 1: failed to parse fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_two_length(uint8_t num_antenna_paths)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+
+	return 1 + (4 * num_tone_data);
+}
+
+static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
+			   uint8_t num_antenna_paths)
+{
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 2: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 2: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
+		ant_perm_index);
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+	size_t len;
+
+	len = 6;
+	if (include_pct)
+		len += 6;
+
+	len += 1 + (4 * num_tone_data);
+
+	return len;
+}
+
+static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
+			     enum cs_role remote_role, bool include_pct,
+			     uint8_t num_antenna_paths)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 3: failed to parse Mode 1 fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 3: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 3: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static bool parse_subevent_header(struct iovec *iov,
+					struct ras_subevent_header *hdr)
+{
+	uint16_t start_acl, freq_comp;
+	uint8_t done_byte, abort_byte, ref_pwr, num_steps;
+
+	if (!util_iov_pull_le16(iov, &start_acl) ||
+	    !util_iov_pull_le16(iov, &freq_comp) ||
+	    !util_iov_pull_u8(iov, &done_byte) ||
+	    !util_iov_pull_u8(iov, &abort_byte) ||
+	    !util_iov_pull_u8(iov, &ref_pwr) ||
+	    !util_iov_pull_u8(iov, &num_steps))
+		return false;
+
+	hdr->start_acl_conn_event = start_acl;
+	hdr->frequency_compensation = freq_comp;
+	hdr->ranging_done_status = RAS_DONE_STATUS_UNPACK_RANGING(done_byte);
+	hdr->subevent_done_status = RAS_DONE_STATUS_UNPACK_SUBEVENT(done_byte);
+	hdr->ranging_abort_reason =
+		RAS_ABORT_REASON_UNPACK_RANGING(abort_byte);
+	hdr->subevent_abort_reason =
+		RAS_ABORT_REASON_UNPACK_SUBEVENT(abort_byte);
+	hdr->reference_power_level = (int8_t)ref_pwr;
+	hdr->num_steps_reported = num_steps;
+
+	return true;
+}
+
+static bool parse_step(struct bt_rap *rap, struct iovec *iov,
+			struct cstracker *reqtracker,
+			uint8_t num_antenna_paths, uint8_t step_idx)
+{
+	uint8_t mode_byte, step_mode;
+	bool include_pct;
+	enum cs_role remote_role;
+	size_t step_payload_len;
+	struct iovec mode_iov;
+	void *payload;
+
+	if (!util_iov_pull_u8(iov, &mode_byte)) {
+		DBG(rap, "Insufficient data for step %u", step_idx);
+		return false;
+	}
+
+	if (mode_byte & RAS_STEP_ABORTED_BIT) {
+		DBG(rap, "  Step %u: mode=%u (aborted)",
+			step_idx, mode_byte & 0x7F);
+		return true;
+	}
+
+	step_mode = mode_byte & 0x7F;
+	include_pct = (reqtracker->rtt_type == 0x01 ||
+			reqtracker->rtt_type == 0x02);
+	remote_role = (reqtracker->role == CS_ROLE_INITIATOR) ?
+			CS_ROLE_REFLECTOR : CS_ROLE_INITIATOR;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		step_payload_len = get_mode_zero_length(remote_role);
+		break;
+	case CS_MODE_ONE:
+		step_payload_len = get_mode_one_length(include_pct);
+		break;
+	case CS_MODE_TWO:
+		step_payload_len = get_mode_two_length(num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		step_payload_len = get_mode_three_length(num_antenna_paths,
+								include_pct);
+		break;
+	default:
+		DBG(rap, "  Step %u: unknown mode=%u", step_idx, step_mode);
+		return true;
+	}
+
+	DBG(rap, "  Step %u: mode=%u payload_len=%zu",
+	    step_idx, step_mode, step_payload_len);
+
+	payload = util_iov_pull(iov, step_payload_len);
+	if (!payload) {
+		DBG(rap, "Insufficient data for step %u payload "
+			"(need %zu, have %zu)",
+			step_idx, step_payload_len, iov->iov_len);
+		return false;
+	}
+
+	mode_iov.iov_base = payload;
+	mode_iov.iov_len = step_payload_len;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		parse_mode_zero(rap, &mode_iov, remote_role);
+		break;
+	case CS_MODE_ONE:
+		parse_mode_one(rap, &mode_iov, remote_role, include_pct);
+		break;
+	case CS_MODE_TWO:
+		parse_mode_two(rap, &mode_iov, num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		parse_mode_three(rap, &mode_iov, remote_role, include_pct,
+						num_antenna_paths);
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
+				struct cstracker *reqtracker,
+				uint8_t num_antenna_paths, uint8_t num_steps)
+{
+	uint8_t i;
+
+	for (i = 0; i < num_steps; i++) {
+		if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
+			break;
+	}
+}
+
+static void parse_ras_data_segments(struct bt_rap *rap,
+			       struct cstracker *reqtracker)
+{
+	struct iovec iov;
+	uint8_t antenna_mask;
+	uint8_t num_antenna_paths;
+
+	if (!rap || !reqtracker)
+		return;
+
+	DBG(rap, "Complete RAS data received: %zu bytes",
+	    reqtracker->segment_data.iov_len);
+
+	antenna_mask =
+		ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
+	num_antenna_paths = antenna_mask_count_paths(antenna_mask);
+
+	iov = reqtracker->segment_data;
+
+	while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
+		struct ras_subevent_header hdr;
+
+		if (!parse_subevent_header(&iov, &hdr))
+			break;
+
+		DBG(rap, "Parsed subevent: start_acl=%u "
+			"freq_comp=%d ref_pwr=%d steps=%u",
+		    hdr.start_acl_conn_event,
+		    hdr.frequency_compensation,
+		    hdr.reference_power_level,
+		    hdr.num_steps_reported);
+
+		parse_subevent_steps(rap, &iov, reqtracker,
+					num_antenna_paths,
+					hdr.num_steps_reported);
+
+		if (hdr.subevent_done_status ==
+		    SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
+		    hdr.ranging_done_status ==
+		    RANGING_DONE_ALL_RESULTS_COMPLETE) {
+			DBG(rap, "Ranging procedure complete");
+			break;
+		}
+	}
+
+	free(reqtracker->segment_data.iov_base);
+	reqtracker->segment_data.iov_base = NULL;
+	reqtracker->segment_data.iov_len = 0;
+}
+
+static bool process_first_segment(struct bt_rap *rap,
+					struct cstracker *reqtracker,
+					struct iovec *iov, bool last_segment)
+{
+	uint16_t counter_config_val;
+	int8_t selected_tx_power;
+	uint8_t antenna_pct;
+
+	if (!util_iov_pull_le16(iov, &counter_config_val) ||
+	    !util_iov_pull_u8(iov, (uint8_t *)&selected_tx_power) ||
+	    !util_iov_pull_u8(iov, &antenna_pct)) {
+		DBG(rap, "First segment too short for ranging header");
+		return false;
+	}
+
+	ranging_header_set_counter(&reqtracker->ranging_header_,
+				counter_config_val & 0x0FFF);
+	ranging_header_set_config_id(&reqtracker->ranging_header_,
+				(counter_config_val >> 12) & 0x0F);
+	reqtracker->ranging_header_.selected_tx_power = selected_tx_power;
+	reqtracker->ranging_header_.antenna_pct = antenna_pct;
+
+	DBG(rap, "First segment: parsed ranging header "
+	    "(counter=%u, config_id=%u, tx_pwr=%d)",
+	    counter_config_val & 0x0FFF,
+	    (counter_config_val >> 12) & 0x0F,
+	    selected_tx_power);
+
+	if (reqtracker->segment_data.iov_base) {
+		free(reqtracker->segment_data.iov_base);
+		reqtracker->segment_data.iov_base = NULL;
+		reqtracker->segment_data.iov_len = 0;
+	}
+
+	if (iov->iov_len > 0) {
+		if (!util_iov_append(&reqtracker->segment_data,
+					iov->iov_base, iov->iov_len)) {
+			DBG(rap, "Failed to initialize segment accumulator");
+			return false;
+		}
+		DBG(rap, "First segment: initialized accumulator "
+		    "with %zu bytes", iov->iov_len);
+	}
+
+	if (!last_segment)
+		return false;
+
+	DBG(rap, "Single-segment: first=1, last=1");
+	return true;
+}
+
+static void ras_realtime_notify_cb(struct bt_rap *rap, uint16_t value_handle,
+				   const uint8_t *value, uint16_t length,
+				   void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	struct segmentation_header seg_hdr;
+	struct cstracker *reqtracker;
+
+	if (!rap || !value || length == 0) {
+		DBG(rap, "Invalid notification data");
+		return;
+	}
+
+	DBG(rap, "Received real-time notification: handle=0x%04x len=%u",
+	    value_handle, length);
+
+	if (!parse_segmentation_header(&iov, &seg_hdr)) {
+		DBG(rap, "Failed to parse segmentation header");
+		return;
+	}
+
+	DBG(rap, "Segment: first=%u last=%u counter=%u",
+	    seg_hdr.first_segment, seg_hdr.last_segment,
+	    seg_hdr.rolling_segment_counter);
+
+	if (!rap->reqtracker) {
+		DBG(rap, "reqtracker is not initialised");
+		return;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	if (seg_hdr.first_segment) {
+		if (!process_first_segment(rap, reqtracker, &iov,
+						seg_hdr.last_segment))
+			return;
+	} else {
+		if (iov.iov_len > 0) {
+			if (!util_iov_append(&reqtracker->segment_data,
+						iov.iov_base, iov.iov_len)) {
+				DBG(rap, "Failed to append segment data");
+				return;
+			}
+			DBG(rap, "Continuation segment: appended "
+			    "%zu bytes (total=%zu)",
+			    iov.iov_len,
+			    reqtracker->segment_data.iov_len);
+		}
+	}
+
+	/* Last segment: parse complete RAS data */
+	if (seg_hdr.last_segment)
+		parse_ras_data_segments(rap, reqtracker);
+}
+
+static void read_ras_features(struct bt_rap *rap, bool success,
+			uint8_t att_ecode,
+			const uint8_t *value, uint16_t length,
+			void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	uint32_t features = 0;
+	struct ras *ras;
+	bool supports_realtime;
+	bool retrieve_lost;
+	bool abort_operation;
+
+	if (!success) {
+		DBG(rap, "Unable to read RAS Features: error 0x%02x",
+			att_ecode);
+		return;
+	}
+
+	ras = rap_get_ras(rap);
+
+	if (length == 0 || length > 4) {
+		DBG(rap, "Invalid RAS Features length: %u (expected 1-4)",
+			length);
+		return;
+	}
+
+	if (length < 4) {
+		uint8_t padded[4] = { 0, 0, 0, 0 };
+
+		memcpy(padded, value, length);
+		iov.iov_base = padded;
+		iov.iov_len = 4;
+		DBG(rap, "RAS Features: short read (%u bytes), zero-pad to 4",
+		    length);
+	}
+
+	if (!util_iov_pull_le32(&iov, &features)) {
+		DBG(rap, "Unable to parse RAS Features value");
+		return;
+	}
+
+	DBG(rap, "RAS Features: 0x%08x", features);
+
+	supports_realtime = (features & 0x01) != 0;
+	retrieve_lost = (features & 0x02) != 0;
+	abort_operation = (features & 0x04) != 0;
+
+	DBG(rap, "RAS Features - Real-time: %s, Retrieve Lost: %s, Abort: %s",
+	    supports_realtime ? "Yes" : "No",
+	    retrieve_lost ? "Yes" : "No",
+	    abort_operation ? "Yes" : "No");
+
+	DBG(rap, "RAS features read successfully, capabilities determined");
+
+	/* Register for real-time characteristic notifications if supported */
+	if (supports_realtime && ras && ras->realtime_chrc && rap->client) {
+		uint16_t value_handle;
+		bt_uuid_t uuid;
+
+		if (gatt_db_attribute_get_char_data(ras->realtime_chrc,
+					NULL, &value_handle,
+					NULL, NULL, &uuid)) {
+			unsigned int notify_id;
+
+			notify_id = bt_rap_register_notify(rap,
+						value_handle,
+						ras_realtime_notify_cb,
+						NULL);
+			if (!notify_id)
+				DBG(rap, "Failed to register for "
+					"real-time notifications");
+			else
+				DBG(rap, "Registered for real-time "
+					"features: id=%u", notify_id);
+		}
+	} else {
+		DBG(rap, "On demand ranging "
+			"remote device - skipping notification "
+			"registration");
+	}
+}
+
 static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct bt_rap *rap = user_data;
@@ -1848,6 +2676,10 @@ static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 			return;
 
 		ras->feat_chrc = attr;
+		if (rap->client) {
+			rap_read_value(rap, value_handle,
+					read_ras_features, rap);
+		}
 	}
 
 	if (!bt_uuid_cmp(&uuid, &uuid_realtime)) {
-- 
2.34.1


^ permalink raw reply related

* [Bug 221552] btmtk: MT7921 (USB 0e8d:e020 / PCI 14c3:7920) Bluetooth broken since 7.0.7 - fix for 0489:e0e2 does not cover this PID
From: bugzilla-daemon @ 2026-05-21  7:53 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <bug-221552-62941@https.bugzilla.kernel.org/>

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

The Linux kernel's regression tracker (Thorsten Leemhuis) (regressions@leemhuis.info) changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |regressions@leemhuis.info

--- Comment #2 from The Linux kernel's regression tracker (Thorsten Leemhuis) (regressions@leemhuis.info) ---
That might be a side-effect from the security fixes that might or might not be
being fixed. Best to try bisect while applying the fix Artem mentioned to find
the root case -- ideally on mainline (and definitely with a vanilla kernel!)

-- 
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

* [PATCH] Bluetooth: hci_conn: Fix memory leak in hci_le_big_terminate()
From: Pavitra Jha @ 2026-05-21  8:04 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, marcel, johan.hedberg, linux-kernel, stable, yang.li,
	Pavitra Jha

hci_le_big_terminate() allocates iso_list_data via kzalloc_obj but
returns 0 without freeing it when neither pa_sync_term nor big_sync_term
flags are set after evaluating the PA and BIG sync connection state.

This early-return path was introduced when hci_le_big_terminate() was
refactored to take struct hci_conn instead of raw u8 parameters, adding
PA/BIG flag evaluation logic. The existing kfree() on hci_cmd_sync_queue
failure does not cover this path.

Fixes: 1ffee96604de ("Bluetooth: Add PA_LINK to distinguish BIG sync and PA sync connections")
Cc: stable@vger.kernel.org
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
---
 net/bluetooth/hci_conn.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 11d3ad8d2..9c5a3dbf8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -803,8 +803,10 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn)
 			d->big_sync_term = true;
 	}
 
-	if (!d->pa_sync_term && !d->big_sync_term)
+	if (!d->pa_sync_term && !d->big_sync_term) {
+		kfree(d);
 		return 0;
+	}
 
 	ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
 				 terminate_big_destroy);
-- 
2.53.0


^ permalink raw reply related

* RE: [v4] Bluetooth: bnep: reject short frames before parsing
From: bluez.test.bot @ 2026-05-21  8:07 UTC (permalink / raw)
  To: linux-bluetooth, rollkingzzc
In-Reply-To: <20260521052611.1815693-1-rollkingzzc@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 936 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=1098453

---Test result---

Test Summary:
CheckPatch                    PASS      0.49 seconds
GitLint                       PASS      0.19 seconds
SubjectPrefix                 PASS      0.06 seconds
BuildKernel                   PASS      19.75 seconds
CheckAllWarning               PASS      25.20 seconds
CheckSparse                   PASS      22.00 seconds
BuildKernel32                 PASS      19.79 seconds
TestRunnerSetup               PASS      412.06 seconds
TestRunner_bnep-tester        PASS      14.59 seconds
IncrementalBuild              PASS      22.14 seconds



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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: [v3] Bluetooth: btusb: Allow firmware re-download when version matches
From: bluez.test.bot @ 2026-05-21  8:09 UTC (permalink / raw)
  To: linux-bluetooth, shuaz
In-Reply-To: <20260521052547.2862803-1-shuaz@qti.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 1654 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=1098452

---Test result---

Test Summary:
CheckPatch                    PASS      0.93 seconds
GitLint                       FAIL      0.33 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      26.12 seconds
CheckAllWarning               PASS      28.54 seconds
CheckSparse                   PASS      27.57 seconds
BuildKernel32                 PASS      25.98 seconds
TestRunnerSetup               PASS      565.39 seconds
IncrementalBuild              PASS      24.67 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v3] Bluetooth: btusb: Allow firmware re-download when version matches

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
34: B1 Line exceeds max length (84>80): "  https://lore.kernel.org/all/20260429121207.1306526-1-shuai.zhang@oss.qualcomm.com/"
40: B1 Line exceeds max length (84>80): "  https://lore.kernel.org/all/20260108074353.1027877-1-shuai.zhang@oss.qualcomm.com/"


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [bluez/bluez] b65a3a: doc: Fix Data Path direction in btmin-le-audio.rst
From: michael kong @ 2026-05-21  8:50 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098468
  Home:   https://github.com/bluez/bluez
  Commit: b65a3aa938b9ef692cc7bcc34f25abe520403936
      https://github.com/bluez/bluez/commit/b65a3aa938b9ef692cc7bcc34f25abe520403936
  Author: michael_kong <kx960506@163.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M doc/btmon-le-audio.rst

  Log Message:
  -----------
  doc: Fix Data Path direction in btmin-le-audio.rst

The Data Path direction 0x01 should be Output (Controller to Host).



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

^ permalink raw reply

* [bluez/bluez] 75a3ce: shared/rap: Add client ranging registration and no...
From: prathibhamadugonde @ 2026-05-21  8:50 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098497
  Home:   https://github.com/bluez/bluez
  Commit: 75a3ce5f2ea1ce327efd2c7506a0644f3923469c
      https://github.com/bluez/bluez/commit/75a3ce5f2ea1ce327efd2c7506a0644f3923469c
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M src/shared/rap.c

  Log Message:
  -----------
  shared/rap: Add client ranging registration and notification parsing

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper fuctions for readablitiy and code indentation.



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

^ permalink raw reply

* [RFC] Bluetooth: L2CAP: possible sock_put leak in l2cap_sock_shutdown
From: stanislavsn1 @ 2026-05-21  8:52 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: marcel, luiz.dentz, Stanislavs Nilovs

From: Stanislavs Nilovs <valorcool@gmail.com>

Hello,

At work I've been applying patches to our Linux Kernel version to fix crash in
L2CAP (more specifically this commit:
https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git/commit/?id=dd6367916d2d).
During review my colleagues have noticed a possible bug in l2cap_sock_shutdown
function which is present even in upstream's master. With above patch there is
the code below:

	/* prevent sk structure from being freed whilst unlocked */
	sock_hold(sk);

	/* prevent chan structure from being freed whilst unlocked */
	chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
	if (!chan)
		goto shutdown_already;

. . .

shutdown_matched:
	l2cap_chan_put(chan);
	sock_put(sk);

shutdown_already:
	if (!err && sk->sk_err)
		err = -sk->sk_err;

	release_sock(sk);

It looks like if l2cap_chan_hold_unless_zero returns a NULL then there is no
sock_put to decrease a refcount.

We have also asked Claude AI to analyze this place and it's response is that
this if case is a dead code and will never happen:

> Now I have what I need. Let me trace the chan reference lifecycle. At socket
> creation (line 1883-1885): l2cap_chan_hold(chan); l2cap_pi(sk)->chan = chan;
>
> The socket holds its own reference to the chan. This reference is only
> dropped in: - l2cap_sock_kill (line 1256) — requires SOCK_ZAPPED set AND
> sk_socket == NULL - l2cap_sock_destruct (line 1757) — called when the socket
> is actually being freed
>
> l2cap_pi(sk)->chan is also never set to NULL during the socket's lifetime —
> l2cap_sock_destruct sets chan->data = NULL but not the reverse pointer.
>
> So for l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan) to return NULL inside
> l2cap_sock_shutdown, the chan's refcount would need to be zero. But the
> socket itself holds a reference, and that reference is only released when the
> socket is being killed or destroyed — neither of which can happen
> concurrently since we're inside lock_sock.
>
> The if (!chan) goto shutdown_already path after sock_hold is effectively
> unreachable. The socket's own reference to the chan guarantees the refcount
> is at least 1 for the entire time l2cap_sock_shutdown is executing under
> lock_sock. The sock_hold imbalance is not a real issue — it's dead code
> guarding against a scenario that cannot occur given the invariants of the
> object lifecycle.
>
> The check is defensive code that was presumably added out of caution when
> l2cap_chan_hold_unless_zero was introduced, but the preconditions make it
> impossible to trigger.

Could you please review and check whether this is intended, an oversight and
truly a dead code, or a real bug?

Regards,
Stanislavs.

^ permalink raw reply

* [bluetooth-next:master] BUILD SUCCESS 0b580042a1a5478b2cf5f1ff66372b75392ad2a4
From: kernel test robot @ 2026-05-21  8:53 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
branch HEAD: 0b580042a1a5478b2cf5f1ff66372b75392ad2a4  Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()

elapsed time: 750m

configs tested: 211
configs skipped: 3

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    clang-16
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    clang-23
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                   randconfig-001-20260521    gcc-8.5.0
arc                   randconfig-002-20260521    gcc-8.5.0
arm                               allnoconfig    clang-23
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    clang-16
arm                              allyesconfig    gcc-15.2.0
arm                                 defconfig    gcc-15.2.0
arm                      footbridge_defconfig    clang-17
arm                   randconfig-001-20260521    gcc-8.5.0
arm                   randconfig-002-20260521    gcc-8.5.0
arm                   randconfig-003-20260521    gcc-8.5.0
arm                   randconfig-004-20260521    gcc-8.5.0
arm64                            allmodconfig    clang-19
arm64                            allmodconfig    clang-23
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260521    gcc-8.5.0
arm64                 randconfig-002-20260521    gcc-8.5.0
arm64                 randconfig-003-20260521    gcc-8.5.0
arm64                 randconfig-004-20260521    gcc-8.5.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260521    gcc-8.5.0
csky                  randconfig-002-20260521    gcc-8.5.0
hexagon                          allmodconfig    clang-17
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    clang-23
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    gcc-15.2.0
hexagon               randconfig-001-20260521    gcc-11.5.0
hexagon               randconfig-002-20260521    gcc-11.5.0
i386                             allmodconfig    clang-20
i386                             allmodconfig    gcc-14
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                             allyesconfig    gcc-14
i386        buildonly-randconfig-001-20260521    clang-20
i386        buildonly-randconfig-002-20260521    clang-20
i386        buildonly-randconfig-003-20260521    clang-20
i386        buildonly-randconfig-004-20260521    clang-20
i386        buildonly-randconfig-005-20260521    clang-20
i386        buildonly-randconfig-006-20260521    clang-20
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260521    clang-20
i386                  randconfig-002-20260521    clang-20
i386                  randconfig-003-20260521    clang-20
i386                  randconfig-004-20260521    clang-20
i386                  randconfig-005-20260521    clang-20
i386                  randconfig-006-20260521    clang-20
i386                  randconfig-007-20260521    clang-20
i386                  randconfig-011-20260521    gcc-14
i386                  randconfig-012-20260521    gcc-14
i386                  randconfig-013-20260521    gcc-14
i386                  randconfig-014-20260521    gcc-14
i386                  randconfig-015-20260521    gcc-14
i386                  randconfig-016-20260521    gcc-14
i386                  randconfig-017-20260521    gcc-14
loongarch                        allmodconfig    clang-19
loongarch                        allmodconfig    clang-23
loongarch                         allnoconfig    clang-23
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch             randconfig-001-20260521    gcc-11.5.0
loongarch             randconfig-002-20260521    gcc-11.5.0
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    clang-16
m68k                             allyesconfig    gcc-15.2.0
m68k                                defconfig    clang-19
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
mips                         rt305x_defconfig    clang-23
nios2                            allmodconfig    clang-23
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    clang-23
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    clang-19
nios2                 randconfig-001-20260521    gcc-11.5.0
nios2                 randconfig-002-20260521    gcc-11.5.0
openrisc                         allmodconfig    clang-23
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    clang-23
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-23
parisc                            allnoconfig    gcc-15.2.0
parisc                           allyesconfig    clang-19
parisc                           allyesconfig    gcc-15.2.0
parisc                              defconfig    gcc-15.2.0
parisc                randconfig-001-20260521    gcc-12.5.0
parisc                randconfig-002-20260521    gcc-12.5.0
parisc64                            defconfig    clang-19
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-23
powerpc                           allnoconfig    gcc-15.2.0
powerpc                      mgcoge_defconfig    clang-23
powerpc               randconfig-001-20260521    gcc-12.5.0
powerpc               randconfig-002-20260521    gcc-12.5.0
powerpc                    sam440ep_defconfig    gcc-15.2.0
powerpc64             randconfig-001-20260521    gcc-12.5.0
powerpc64             randconfig-002-20260521    gcc-12.5.0
riscv                            allmodconfig    clang-23
riscv                             allnoconfig    clang-23
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    gcc-15.2.0
riscv                 randconfig-001-20260521    gcc-15.2.0
riscv                 randconfig-002-20260521    gcc-15.2.0
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    gcc-15.2.0
s390                  randconfig-001-20260521    gcc-15.2.0
s390                  randconfig-002-20260521    gcc-15.2.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-23
sh                                allnoconfig    gcc-15.2.0
sh                               allyesconfig    clang-19
sh                               allyesconfig    gcc-15.2.0
sh                                  defconfig    gcc-14
sh                    randconfig-001-20260521    gcc-15.2.0
sh                    randconfig-002-20260521    gcc-15.2.0
sparc                             allnoconfig    clang-23
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-15.2.0
sparc                 randconfig-001-20260521    gcc-8.5.0
sparc                 randconfig-002-20260521    gcc-8.5.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    gcc-14
sparc64               randconfig-001-20260521    gcc-8.5.0
sparc64               randconfig-002-20260521    gcc-8.5.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260521    gcc-8.5.0
um                    randconfig-002-20260521    gcc-8.5.0
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                            allnoconfig    clang-23
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260521    clang-20
x86_64      buildonly-randconfig-002-20260521    clang-20
x86_64      buildonly-randconfig-003-20260521    clang-20
x86_64      buildonly-randconfig-004-20260521    clang-20
x86_64      buildonly-randconfig-005-20260521    clang-20
x86_64      buildonly-randconfig-006-20260521    clang-20
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                randconfig-001-20260521    clang-20
x86_64                randconfig-002-20260521    clang-20
x86_64                randconfig-003-20260521    clang-20
x86_64                randconfig-004-20260521    clang-20
x86_64                randconfig-005-20260521    clang-20
x86_64                randconfig-006-20260521    clang-20
x86_64                         randconfig-011    gcc-14
x86_64                randconfig-011-20260521    gcc-14
x86_64                         randconfig-012    gcc-14
x86_64                randconfig-012-20260521    gcc-14
x86_64                         randconfig-013    gcc-14
x86_64                randconfig-013-20260521    gcc-14
x86_64                         randconfig-014    gcc-14
x86_64                randconfig-014-20260521    gcc-14
x86_64                         randconfig-015    gcc-14
x86_64                randconfig-015-20260521    gcc-14
x86_64                         randconfig-016    gcc-14
x86_64                randconfig-016-20260521    gcc-14
x86_64                         randconfig-071    clang-20
x86_64                randconfig-071-20260521    clang-20
x86_64                         randconfig-072    clang-20
x86_64                randconfig-072-20260521    clang-20
x86_64                         randconfig-073    clang-20
x86_64                randconfig-073-20260521    clang-20
x86_64                         randconfig-074    clang-20
x86_64                randconfig-074-20260521    clang-20
x86_64                         randconfig-075    clang-20
x86_64                randconfig-075-20260521    clang-20
x86_64                         randconfig-076    clang-20
x86_64                randconfig-076-20260521    clang-20
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-23
xtensa                            allnoconfig    gcc-15.2.0
xtensa                           allyesconfig    clang-23
xtensa                           allyesconfig    gcc-15.2.0
xtensa                randconfig-001-20260521    gcc-8.5.0
xtensa                randconfig-002-20260521    gcc-8.5.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* Re: [PATCH v3 0/9] Fixes/improvements for the PCI M.2 power sequencing driver
From: Bartosz Golaszewski @ 2026-05-21  9:18 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-pci, linux-pm,
	linux-kernel, linux-arm-msm, linux-bluetooth, Wei Deng,
	Konrad Dybcio, Bartosz Golaszewski, Dmitry Baryshkov,
	Manivannan Sadhasivam, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz
In-Reply-To: <20260519-pwrseq-m2-bt-v3-0-b39dc2ae3966@oss.qualcomm.com>

On Tue, 19 May 2026 10:55:55 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> Hi,
>
> This series has several key improvements and fixes to the M.2 power sequencing
> driver and also the BT HCI_QCA driver. Notably, this series allows the M.2 power
> sequencing driver to work with more M.2 cards, not just WCN7850. It also allows
> the BT HCI_QCA driver to detect whether it can control BT_EN (or W_DISABLE2#)
> signal on the connector and set the HCI_QUIRK_NON_PERSISTENT_SETUP quirk.
>
> Testing
> =======
>
> This series was tested on Lenovo Thinkpad T14s together with the below DTS
> patches:
> https://github.com/Mani-Sadhasivam/linux/commit/29534d15307551b2355eb254601dec511169f0aa
> https://github.com/Mani-Sadhasivam/linux/commit/f4eaacfe647674be200847092b43cdef2194fc55
>
> Merge Strategy
> ==============
>
> Since the BT HCI_QCA changes depend on the pwrseq changes, it would be good to
> merge the whole series through pwrseq tree or through an immutable branch.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---

Luiz, Augusto: this series looks good to me. Do you want me to queue the pwrseq
patches on an immutable branch or should I just take the entire thing?

Bart

^ permalink raw reply

* RE: [BlueZ,v2] shared/rap: Add client ranging registration and notification parsing
From: bluez.test.bot @ 2026-05-21  9:42 UTC (permalink / raw)
  To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260521071821.2355923-1-prathm@qti.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 2281 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=1098497

---Test result---

Test Summary:
CheckPatch                    FAIL      1.01 seconds
GitLint                       PASS      0.34 seconds
BuildEll                      PASS      20.39 seconds
BluezMake                     PASS      609.40 seconds
MakeCheck                     PASS      1.18 seconds
MakeDistcheck                 FAIL      215.51 seconds
CheckValgrind                 PASS      200.78 seconds
CheckSmatch                   PASS      322.84 seconds
bluezmakeextell               PASS      164.82 seconds
IncrementalBuild              PASS      606.23 seconds
ScanBuild                     PASS      942.07 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v2] shared/rap: Add client ranging registration and notification parsing
WARNING:TYPO_SPELLING: 'fuctions' may be misspelled - perhaps 'functions'?
#161: 
Use helper fuctions for readablitiy and code indentation.
           ^^^^^^^^

/github/workspace/src/patch/14585921.patch total: 0 errors, 1 warnings, 935 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14585921.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

make[4]: *** [Makefile:10239: test-suite.log] Error 1
make[3]: *** [Makefile:10347: check-TESTS] Error 2
make[2]: *** [Makefile:10818: check-am] Error 2
make[1]: *** [Makefile:10820: check] Error 2
make: *** [Makefile:10741: distcheck] Error 1


https://github.com/bluez/bluez/pull/2141

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: [BlueZ] doc: Fix Data Path direction in btmin-le-audio.rst
From: bluez.test.bot @ 2026-05-21  9:48 UTC (permalink / raw)
  To: linux-bluetooth, kx960506
In-Reply-To: <20260521060919.2124-1-kx960506@163.com>

[-- Attachment #1: Type: text/plain, Size: 2160 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=1098468

---Test result---

Test Summary:
CheckPatch                    PASS      0.32 seconds
GitLint                       PASS      0.25 seconds
BuildEll                      PASS      20.22 seconds
BluezMake                     PASS      646.79 seconds
MakeCheck                     FAIL      18.66 seconds
MakeDistcheck                 FAIL      230.73 seconds
CheckValgrind                 FAIL      292.20 seconds
CheckSmatch                   PASS      354.07 seconds
bluezmakeextell               PASS      181.71 seconds
IncrementalBuild              PASS      648.04 seconds
ScanBuild                     PASS      1021.10 seconds

Details
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

make[3]: *** [Makefile:10239: test-suite.log] Error 1
make[2]: *** [Makefile:10347: check-TESTS] Error 2
make[1]: *** [Makefile:10818: check-am] Error 2
make: *** [Makefile:10820: check] Error 2
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

make[4]: *** [Makefile:10239: test-suite.log] Error 1
make[3]: *** [Makefile:10347: check-TESTS] Error 2
make[2]: *** [Makefile:10818: check-am] Error 2
make[1]: *** [Makefile:10820: check] Error 2
make: *** [Makefile:10741: distcheck] Error 1
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12990:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12990 | int main(int argc, char *argv[])
      |     ^~~~
make[3]: *** [Makefile:10239: test-suite.log] Error 1
make[2]: *** [Makefile:10347: check-TESTS] Error 2
make[1]: *** [Makefile:10818: check-am] Error 2
make: *** [Makefile:10820: check] Error 2


https://github.com/bluez/bluez/pull/2140

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH BlueZ v2] shared/rap: Add client ranging registration and notification parsing
From: Prathibha Madugonde @ 2026-05-21 10:32 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper functions for readablitiy and code indentation.

---
 src/shared/rap.c | 836 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 834 insertions(+), 2 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index 145da2060..3639c3f4d 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -29,6 +29,9 @@
 #define DBG(_rap, fmt, ...) \
 	rap_debug(_rap, "%s:%s() " fmt, __FILE__, __func__, ##__VA_ARGS__)
 
+#define SIGN_EXTEND_TO_16(val, bits) \
+	((int16_t)(((val) ^ (1U << ((bits)-1))) - (1U << ((bits)-1))))
+
 #define RAS_UUID16			0x185B
 
 /* Total number of attribute handles reserved for the RAS service */
@@ -43,6 +46,11 @@
 #define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
 #define RAS_SUBEVENT_HEADER_SIZE 8
 
+#define CS_MODE_ZERO_WIRE_INIT_SIZE 7
+#define CS_MODE_ZERO_WIRE_REF_SIZE 3
+#define CS_MODE_ONE_WIRE_SIZE_MIN 6
+#define CS_MODE_ONE_WIRE_SIZE_MAX 12
+
 enum pct_format {
 	IQ = 0,
 	PHASE = 1,
@@ -134,6 +142,28 @@ static inline void ranging_header_set_pct_format(struct ranging_header *hdr,
 				((format & 0x03) << 6);
 }
 
+static inline uint8_t ranging_header_get_antenna_mask(
+					const struct ranging_header *hdr)
+{
+	if (!hdr)
+		return 0;
+
+	return hdr->antenna_pct & 0x0F;
+}
+
+static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
+{
+	uint8_t count = 0;
+	uint8_t i;
+
+	for (i = 0; i < 4; i++) {
+		if (antenna_mask & (1u << i))
+			count++;
+	}
+
+	return count;
+}
+
 struct ras_subevent_header {
 	uint16_t start_acl_conn_event;
 	uint16_t frequency_compensation;
@@ -149,9 +179,21 @@ struct ras_subevent_header {
 #define RAS_DONE_STATUS_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_DONE_STATUS_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_DONE_STATUS_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 #define RAS_ABORT_REASON_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_ABORT_REASON_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_ABORT_REASON_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 struct ras_subevent {
 	struct ras_subevent_header subevent_header;
 	uint8_t subevent_data[];
@@ -206,6 +248,11 @@ struct cstracker {
 	uint16_t last_start_acl_conn_evt_counter;
 	uint16_t last_freq_comp;
 	int8_t last_ref_pwr_lvl;
+
+	/* Client - first segment carries this */
+	struct ranging_header   ranging_header_;
+	/* Client - subsequent segments appended using iovec */
+	struct iovec segment_data;
 };
 
 /* Ranging Service context */
@@ -254,6 +301,7 @@ struct bt_rap {
 	void *debug_data;
 	void *user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 };
 
 static struct queue *rap_db;
@@ -274,6 +322,28 @@ struct bt_rap_ready {
 	void *data;
 };
 
+typedef void (*rap_notify_t)(struct bt_rap *rap, uint16_t value_handle,
+			const uint8_t *value, uint16_t length,
+			void *user_data);
+
+struct bt_rap_notify {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_notify_t func;
+	void *user_data;
+};
+
+typedef void (*rap_func_t)(struct bt_rap *rap, bool success,
+			uint8_t att_ecode, const uint8_t *value,
+			uint16_t length, void *user_data);
+
+struct bt_rap_pending {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_func_t func;
+	void *userdata;
+};
+
 uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
 uint8_t ras_segment_header_size = 1;
 
@@ -542,6 +612,11 @@ static void rap_free(void *data)
 		rap->resptracker = NULL;
 	}
 
+	if (rap->reqtracker) {
+		free(rap->reqtracker);
+		rap->reqtracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -641,6 +716,12 @@ static void cs_tracker_init(struct cstracker *t)
 	t->last_start_acl_conn_evt_counter = 0;
 	t->last_freq_comp = 0;
 	t->last_ref_pwr_lvl = 0;
+
+	/* Initialize ranging header using helper functions */
+	memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
+	/* Initialize segment accumulator */
+	t->segment_data.iov_base = NULL;
+	t->segment_data.iov_len = 0;
 }
 
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
@@ -1698,6 +1779,42 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
 		cont->step_data);
 }
 
+static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result_cont *cont,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+}
+
+static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+
+}
+
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
@@ -1707,7 +1824,12 @@ void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
 
-	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result_cont(rap, cont,
+								length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
@@ -1720,7 +1842,11 @@ void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 	DBG(rap, "Received CS subevent: len=%d", length);
 
 	/* Populate CsProcedureData and send RAS payload */
-	form_ras_data_with_cs_subevent_result(rap, data, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result(rap, data, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
@@ -1763,6 +1889,7 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 
 	if (!rap)
 		return;
@@ -1781,6 +1908,19 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	resptracker->config_id = data->config_id;
 	resptracker->role = data->role;
 	resptracker->rtt_type = data->rtt_type;
+
+	if (!rap->reqtracker) {
+		reqtracker = new0(struct cstracker, 1);
+		cs_tracker_init(reqtracker);
+		rap->reqtracker = reqtracker;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	/* Basic fields */
+	reqtracker->config_id = data->config_id;
+	reqtracker->role = data->role;
+	reqtracker->rtt_type = data->rtt_type;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -1815,6 +1955,694 @@ done:
 	return rap;
 }
 
+static void ras_pending_destroy(void *data)
+{
+	struct bt_rap_pending *pending = data;
+	struct bt_rap *rap = pending->rap;
+
+	if (queue_remove_if(rap->pending, NULL, pending))
+		free(pending);
+}
+
+static void ras_pending_complete(bool success, uint8_t att_ecode,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct bt_rap_pending *pending = user_data;
+
+	if (pending->func)
+		pending->func(pending->rap, success, att_ecode, value, length,
+					  pending->userdata);
+}
+
+static void rap_read_value(struct bt_rap *rap, uint16_t value_handle,
+			   rap_func_t func, void *user_data)
+{
+	struct bt_rap_pending *pending;
+
+	pending = new0(struct bt_rap_pending, 1);
+	pending->rap = rap;
+	pending->func = func;
+	pending->userdata = user_data;
+
+	pending->id = bt_gatt_client_read_value(rap->client, value_handle,
+						ras_pending_complete, pending,
+						ras_pending_destroy);
+	if (!pending->id) {
+		DBG(rap, "Unable to send Read request");
+		free(pending);
+		return;
+	}
+
+	queue_push_tail(rap->pending, pending);
+}
+
+static void ras_register(uint16_t att_ecode, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (att_ecode)
+		DBG(notify->rap, "RAS register failed 0x%04x", att_ecode);
+
+	(void)notify;
+}
+
+static void rap_notify(uint16_t value_handle, const uint8_t *value,
+				uint16_t length, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (notify->func)
+		notify->func(notify->rap, value_handle, value, length,
+					 notify->user_data);
+}
+
+static void rap_notify_destroy(void *data)
+{
+	struct bt_rap_notify *notify = data;
+	struct bt_rap *rap = notify->rap;
+
+	if (queue_remove_if(rap->notify, NULL, notify))
+		free(notify);
+}
+
+static unsigned int bt_rap_register_notify(struct bt_rap *rap,
+					uint16_t value_handle,
+					rap_notify_t func,
+					void *user_data)
+{
+	struct bt_rap_notify *notify;
+
+	notify = new0(struct bt_rap_notify, 1);
+	notify->rap = rap;
+	notify->func = func;
+	notify->user_data = user_data;
+
+	DBG(rap, "register for notifications");
+
+	notify->id = bt_gatt_client_register_notify(rap->client,
+					value_handle, ras_register,
+					rap_notify, notify,
+					rap_notify_destroy);
+	if (!notify->id) {
+		DBG(rap, "Unable to register for notifications");
+		free(notify);
+		return 0;
+	}
+
+	queue_push_tail(rap->notify, notify);
+
+	return notify->id;
+}
+
+static inline bool parse_segmentation_header(struct iovec *iov,
+					     struct segmentation_header *s)
+{
+	uint8_t byte;
+
+	if (!util_iov_pull_u8(iov, &byte))
+		return false;
+
+	s->first_segment = (byte & 0x01) ? 1 : 0;
+	s->last_segment = (byte & 0x02) ? 1 : 0;
+	s->rolling_segment_counter = (byte >> 2) & 0x3F;
+
+	return true;
+}
+
+static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
+				int16_t *q_sample)
+{
+	uint32_t buffer;
+	uint32_t i12;
+	uint32_t q12;
+
+	if (!util_iov_pull_le24(iov, &buffer)) {
+		*i_sample = 0;
+		*q_sample = 0;
+		return;
+	}
+
+	i12 =  buffer        & 0x0FFFU;   /* bits 0..11 */
+	q12 = (buffer >> 12) & 0x0FFFU;   /* bits 12..23 */
+
+	*i_sample = SIGN_EXTEND_TO_16(i12, 12);
+	*q_sample = SIGN_EXTEND_TO_16(q12, 12);
+}
+
+static size_t get_mode_zero_length(enum cs_role remote_role)
+{
+	return (remote_role == CS_ROLE_INITIATOR) ?
+		CS_MODE_ZERO_WIRE_INIT_SIZE :
+		CS_MODE_ZERO_WIRE_REF_SIZE;
+}
+
+static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
+			    enum cs_role remote_role)
+{
+	uint8_t packet_quality;
+	int8_t packet_rssi_dbm;
+	uint8_t packet_ant;
+	uint32_t init_measured_freq_offset = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 0: failed to parse common fields");
+		return;
+	}
+
+	if (remote_role == CS_ROLE_INITIATOR) {
+		if (!util_iov_pull_le32(mode_iov, &init_measured_freq_offset)) {
+			DBG(rap, "Mode 0: failed to parse freq offset");
+			return;
+		}
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_one_length(bool include_pct)
+{
+	if (include_pct)
+		return CS_MODE_ONE_WIRE_SIZE_MAX;
+	return CS_MODE_ONE_WIRE_SIZE_MIN;
+}
+
+static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
+			   enum cs_role remote_role, bool include_pct)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 1: failed to parse fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_two_length(uint8_t num_antenna_paths)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+
+	return 1 + (4 * num_tone_data);
+}
+
+static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
+			   uint8_t num_antenna_paths)
+{
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 2: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 2: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
+		ant_perm_index);
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+	size_t len;
+
+	len = 6;
+	if (include_pct)
+		len += 6;
+
+	len += 1 + (4 * num_tone_data);
+
+	return len;
+}
+
+static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
+			     enum cs_role remote_role, bool include_pct,
+			     uint8_t num_antenna_paths)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 3: failed to parse Mode 1 fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 3: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 3: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static bool parse_subevent_header(struct iovec *iov,
+					struct ras_subevent_header *hdr)
+{
+	uint16_t start_acl, freq_comp;
+	uint8_t done_byte, abort_byte, ref_pwr, num_steps;
+
+	if (!util_iov_pull_le16(iov, &start_acl) ||
+	    !util_iov_pull_le16(iov, &freq_comp) ||
+	    !util_iov_pull_u8(iov, &done_byte) ||
+	    !util_iov_pull_u8(iov, &abort_byte) ||
+	    !util_iov_pull_u8(iov, &ref_pwr) ||
+	    !util_iov_pull_u8(iov, &num_steps))
+		return false;
+
+	hdr->start_acl_conn_event = start_acl;
+	hdr->frequency_compensation = freq_comp;
+	hdr->ranging_done_status = RAS_DONE_STATUS_UNPACK_RANGING(done_byte);
+	hdr->subevent_done_status = RAS_DONE_STATUS_UNPACK_SUBEVENT(done_byte);
+	hdr->ranging_abort_reason =
+		RAS_ABORT_REASON_UNPACK_RANGING(abort_byte);
+	hdr->subevent_abort_reason =
+		RAS_ABORT_REASON_UNPACK_SUBEVENT(abort_byte);
+	hdr->reference_power_level = (int8_t)ref_pwr;
+	hdr->num_steps_reported = num_steps;
+
+	return true;
+}
+
+static bool parse_step(struct bt_rap *rap, struct iovec *iov,
+			struct cstracker *reqtracker,
+			uint8_t num_antenna_paths, uint8_t step_idx)
+{
+	uint8_t mode_byte, step_mode;
+	bool include_pct;
+	enum cs_role remote_role;
+	size_t step_payload_len;
+	struct iovec mode_iov;
+	void *payload;
+
+	if (!util_iov_pull_u8(iov, &mode_byte)) {
+		DBG(rap, "Insufficient data for step %u", step_idx);
+		return false;
+	}
+
+	if (mode_byte & RAS_STEP_ABORTED_BIT) {
+		DBG(rap, "  Step %u: mode=%u (aborted)",
+			step_idx, mode_byte & 0x7F);
+		return true;
+	}
+
+	step_mode = mode_byte & 0x7F;
+	include_pct = (reqtracker->rtt_type == 0x01 ||
+			reqtracker->rtt_type == 0x02);
+	remote_role = (reqtracker->role == CS_ROLE_INITIATOR) ?
+			CS_ROLE_REFLECTOR : CS_ROLE_INITIATOR;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		step_payload_len = get_mode_zero_length(remote_role);
+		break;
+	case CS_MODE_ONE:
+		step_payload_len = get_mode_one_length(include_pct);
+		break;
+	case CS_MODE_TWO:
+		step_payload_len = get_mode_two_length(num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		step_payload_len = get_mode_three_length(num_antenna_paths,
+								include_pct);
+		break;
+	default:
+		DBG(rap, "  Step %u: unknown mode=%u", step_idx, step_mode);
+		return true;
+	}
+
+	DBG(rap, "  Step %u: mode=%u payload_len=%zu",
+	    step_idx, step_mode, step_payload_len);
+
+	payload = util_iov_pull(iov, step_payload_len);
+	if (!payload) {
+		DBG(rap, "Insufficient data for step %u payload "
+			"(need %zu, have %zu)",
+			step_idx, step_payload_len, iov->iov_len);
+		return false;
+	}
+
+	mode_iov.iov_base = payload;
+	mode_iov.iov_len = step_payload_len;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		parse_mode_zero(rap, &mode_iov, remote_role);
+		break;
+	case CS_MODE_ONE:
+		parse_mode_one(rap, &mode_iov, remote_role, include_pct);
+		break;
+	case CS_MODE_TWO:
+		parse_mode_two(rap, &mode_iov, num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		parse_mode_three(rap, &mode_iov, remote_role, include_pct,
+						num_antenna_paths);
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
+				struct cstracker *reqtracker,
+				uint8_t num_antenna_paths, uint8_t num_steps)
+{
+	uint8_t i;
+
+	for (i = 0; i < num_steps; i++) {
+		if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
+			break;
+	}
+}
+
+static void parse_ras_data_segments(struct bt_rap *rap,
+			       struct cstracker *reqtracker)
+{
+	struct iovec iov;
+	uint8_t antenna_mask;
+	uint8_t num_antenna_paths;
+
+	if (!rap || !reqtracker)
+		return;
+
+	DBG(rap, "Complete RAS data received: %zu bytes",
+	    reqtracker->segment_data.iov_len);
+
+	antenna_mask =
+		ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
+	num_antenna_paths = antenna_mask_count_paths(antenna_mask);
+
+	iov = reqtracker->segment_data;
+
+	while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
+		struct ras_subevent_header hdr;
+
+		if (!parse_subevent_header(&iov, &hdr))
+			break;
+
+		DBG(rap, "Parsed subevent: start_acl=%u "
+			"freq_comp=%d ref_pwr=%d steps=%u",
+		    hdr.start_acl_conn_event,
+		    hdr.frequency_compensation,
+		    hdr.reference_power_level,
+		    hdr.num_steps_reported);
+
+		parse_subevent_steps(rap, &iov, reqtracker,
+					num_antenna_paths,
+					hdr.num_steps_reported);
+
+		if (hdr.subevent_done_status ==
+		    SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
+		    hdr.ranging_done_status ==
+		    RANGING_DONE_ALL_RESULTS_COMPLETE) {
+			DBG(rap, "Ranging procedure complete");
+			break;
+		}
+	}
+
+	free(reqtracker->segment_data.iov_base);
+	reqtracker->segment_data.iov_base = NULL;
+	reqtracker->segment_data.iov_len = 0;
+}
+
+static bool process_first_segment(struct bt_rap *rap,
+					struct cstracker *reqtracker,
+					struct iovec *iov, bool last_segment)
+{
+	uint16_t counter_config_val;
+	int8_t selected_tx_power;
+	uint8_t antenna_pct;
+
+	if (!util_iov_pull_le16(iov, &counter_config_val) ||
+	    !util_iov_pull_u8(iov, (uint8_t *)&selected_tx_power) ||
+	    !util_iov_pull_u8(iov, &antenna_pct)) {
+		DBG(rap, "First segment too short for ranging header");
+		return false;
+	}
+
+	ranging_header_set_counter(&reqtracker->ranging_header_,
+				counter_config_val & 0x0FFF);
+	ranging_header_set_config_id(&reqtracker->ranging_header_,
+				(counter_config_val >> 12) & 0x0F);
+	reqtracker->ranging_header_.selected_tx_power = selected_tx_power;
+	reqtracker->ranging_header_.antenna_pct = antenna_pct;
+
+	DBG(rap, "First segment: parsed ranging header "
+	    "(counter=%u, config_id=%u, tx_pwr=%d)",
+	    counter_config_val & 0x0FFF,
+	    (counter_config_val >> 12) & 0x0F,
+	    selected_tx_power);
+
+	if (reqtracker->segment_data.iov_base) {
+		free(reqtracker->segment_data.iov_base);
+		reqtracker->segment_data.iov_base = NULL;
+		reqtracker->segment_data.iov_len = 0;
+	}
+
+	if (iov->iov_len > 0) {
+		if (!util_iov_append(&reqtracker->segment_data,
+					iov->iov_base, iov->iov_len)) {
+			DBG(rap, "Failed to initialize segment accumulator");
+			return false;
+		}
+		DBG(rap, "First segment: initialized accumulator "
+		    "with %zu bytes", iov->iov_len);
+	}
+
+	if (!last_segment)
+		return false;
+
+	DBG(rap, "Single-segment: first=1, last=1");
+	return true;
+}
+
+static void ras_realtime_notify_cb(struct bt_rap *rap, uint16_t value_handle,
+				   const uint8_t *value, uint16_t length,
+				   void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	struct segmentation_header seg_hdr;
+	struct cstracker *reqtracker;
+
+	if (!rap || !value || length == 0) {
+		DBG(rap, "Invalid notification data");
+		return;
+	}
+
+	DBG(rap, "Received real-time notification: handle=0x%04x len=%u",
+	    value_handle, length);
+
+	if (!parse_segmentation_header(&iov, &seg_hdr)) {
+		DBG(rap, "Failed to parse segmentation header");
+		return;
+	}
+
+	DBG(rap, "Segment: first=%u last=%u counter=%u",
+	    seg_hdr.first_segment, seg_hdr.last_segment,
+	    seg_hdr.rolling_segment_counter);
+
+	if (!rap->reqtracker) {
+		DBG(rap, "reqtracker is not initialised");
+		return;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	if (seg_hdr.first_segment) {
+		if (!process_first_segment(rap, reqtracker, &iov,
+						seg_hdr.last_segment))
+			return;
+	} else {
+		if (iov.iov_len > 0) {
+			if (!util_iov_append(&reqtracker->segment_data,
+						iov.iov_base, iov.iov_len)) {
+				DBG(rap, "Failed to append segment data");
+				return;
+			}
+			DBG(rap, "Continuation segment: appended "
+			    "%zu bytes (total=%zu)",
+			    iov.iov_len,
+			    reqtracker->segment_data.iov_len);
+		}
+	}
+
+	/* Last segment: parse complete RAS data */
+	if (seg_hdr.last_segment)
+		parse_ras_data_segments(rap, reqtracker);
+}
+
+static void read_ras_features(struct bt_rap *rap, bool success,
+			uint8_t att_ecode,
+			const uint8_t *value, uint16_t length,
+			void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	uint32_t features = 0;
+	struct ras *ras;
+	bool supports_realtime;
+	bool retrieve_lost;
+	bool abort_operation;
+
+	if (!success) {
+		DBG(rap, "Unable to read RAS Features: error 0x%02x",
+			att_ecode);
+		return;
+	}
+
+	ras = rap_get_ras(rap);
+
+	if (length == 0 || length > 4) {
+		DBG(rap, "Invalid RAS Features length: %u (expected 1-4)",
+			length);
+		return;
+	}
+
+	if (length < 4) {
+		uint8_t padded[4] = { 0, 0, 0, 0 };
+
+		memcpy(padded, value, length);
+		iov.iov_base = padded;
+		iov.iov_len = 4;
+		DBG(rap, "RAS Features: short read (%u bytes), zero-pad to 4",
+		    length);
+	}
+
+	if (!util_iov_pull_le32(&iov, &features)) {
+		DBG(rap, "Unable to parse RAS Features value");
+		return;
+	}
+
+	DBG(rap, "RAS Features: 0x%08x", features);
+
+	supports_realtime = (features & 0x01) != 0;
+	retrieve_lost = (features & 0x02) != 0;
+	abort_operation = (features & 0x04) != 0;
+
+	DBG(rap, "RAS Features - Real-time: %s, Retrieve Lost: %s, Abort: %s",
+	    supports_realtime ? "Yes" : "No",
+	    retrieve_lost ? "Yes" : "No",
+	    abort_operation ? "Yes" : "No");
+
+	DBG(rap, "RAS features read successfully, capabilities determined");
+
+	/* Register for real-time characteristic notifications if supported */
+	if (supports_realtime && ras && ras->realtime_chrc && rap->client) {
+		uint16_t value_handle;
+		bt_uuid_t uuid;
+
+		if (gatt_db_attribute_get_char_data(ras->realtime_chrc,
+					NULL, &value_handle,
+					NULL, NULL, &uuid)) {
+			unsigned int notify_id;
+
+			notify_id = bt_rap_register_notify(rap,
+						value_handle,
+						ras_realtime_notify_cb,
+						NULL);
+			if (!notify_id)
+				DBG(rap, "Failed to register for "
+					"real-time notifications");
+			else
+				DBG(rap, "Registered for real-time "
+					"features: id=%u", notify_id);
+		}
+	} else {
+		DBG(rap, "On demand ranging "
+			"remote device - skipping notification "
+			"registration");
+	}
+}
+
 static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct bt_rap *rap = user_data;
@@ -1848,6 +2676,10 @@ static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 			return;
 
 		ras->feat_chrc = attr;
+		if (rap->client) {
+			rap_read_value(rap, value_handle,
+					read_ras_features, rap);
+		}
 	}
 
 	if (!bt_uuid_cmp(&uuid, &uuid_realtime)) {
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v4] shared/rap: Add client ranging registration and notification parsing
From: Prathibha Madugonde @ 2026-05-21 11:15 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper functions for readablitiy and code indentation.

---
 src/shared/rap.c | 836 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 834 insertions(+), 2 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index 145da2060..3639c3f4d 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -29,6 +29,9 @@
 #define DBG(_rap, fmt, ...) \
 	rap_debug(_rap, "%s:%s() " fmt, __FILE__, __func__, ##__VA_ARGS__)
 
+#define SIGN_EXTEND_TO_16(val, bits) \
+	((int16_t)(((val) ^ (1U << ((bits)-1))) - (1U << ((bits)-1))))
+
 #define RAS_UUID16			0x185B
 
 /* Total number of attribute handles reserved for the RAS service */
@@ -43,6 +46,11 @@
 #define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
 #define RAS_SUBEVENT_HEADER_SIZE 8
 
+#define CS_MODE_ZERO_WIRE_INIT_SIZE 7
+#define CS_MODE_ZERO_WIRE_REF_SIZE 3
+#define CS_MODE_ONE_WIRE_SIZE_MIN 6
+#define CS_MODE_ONE_WIRE_SIZE_MAX 12
+
 enum pct_format {
 	IQ = 0,
 	PHASE = 1,
@@ -134,6 +142,28 @@ static inline void ranging_header_set_pct_format(struct ranging_header *hdr,
 				((format & 0x03) << 6);
 }
 
+static inline uint8_t ranging_header_get_antenna_mask(
+					const struct ranging_header *hdr)
+{
+	if (!hdr)
+		return 0;
+
+	return hdr->antenna_pct & 0x0F;
+}
+
+static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
+{
+	uint8_t count = 0;
+	uint8_t i;
+
+	for (i = 0; i < 4; i++) {
+		if (antenna_mask & (1u << i))
+			count++;
+	}
+
+	return count;
+}
+
 struct ras_subevent_header {
 	uint16_t start_acl_conn_event;
 	uint16_t frequency_compensation;
@@ -149,9 +179,21 @@ struct ras_subevent_header {
 #define RAS_DONE_STATUS_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_DONE_STATUS_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_DONE_STATUS_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 #define RAS_ABORT_REASON_PACK(ranging, subevent) \
 	((uint8_t)(((ranging) & 0x0F) | (((subevent) & 0x0F) << 4)))
 
+#define RAS_ABORT_REASON_UNPACK_RANGING(packed) \
+	((uint8_t)((packed) & 0x0F))
+
+#define RAS_ABORT_REASON_UNPACK_SUBEVENT(packed) \
+	((uint8_t)(((packed) >> 4) & 0x0F))
+
 struct ras_subevent {
 	struct ras_subevent_header subevent_header;
 	uint8_t subevent_data[];
@@ -206,6 +248,11 @@ struct cstracker {
 	uint16_t last_start_acl_conn_evt_counter;
 	uint16_t last_freq_comp;
 	int8_t last_ref_pwr_lvl;
+
+	/* Client - first segment carries this */
+	struct ranging_header   ranging_header_;
+	/* Client - subsequent segments appended using iovec */
+	struct iovec segment_data;
 };
 
 /* Ranging Service context */
@@ -254,6 +301,7 @@ struct bt_rap {
 	void *debug_data;
 	void *user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 };
 
 static struct queue *rap_db;
@@ -274,6 +322,28 @@ struct bt_rap_ready {
 	void *data;
 };
 
+typedef void (*rap_notify_t)(struct bt_rap *rap, uint16_t value_handle,
+			const uint8_t *value, uint16_t length,
+			void *user_data);
+
+struct bt_rap_notify {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_notify_t func;
+	void *user_data;
+};
+
+typedef void (*rap_func_t)(struct bt_rap *rap, bool success,
+			uint8_t att_ecode, const uint8_t *value,
+			uint16_t length, void *user_data);
+
+struct bt_rap_pending {
+	unsigned int id;
+	struct bt_rap *rap;
+	rap_func_t func;
+	void *userdata;
+};
+
 uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
 uint8_t ras_segment_header_size = 1;
 
@@ -542,6 +612,11 @@ static void rap_free(void *data)
 		rap->resptracker = NULL;
 	}
 
+	if (rap->reqtracker) {
+		free(rap->reqtracker);
+		rap->reqtracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -641,6 +716,12 @@ static void cs_tracker_init(struct cstracker *t)
 	t->last_start_acl_conn_evt_counter = 0;
 	t->last_freq_comp = 0;
 	t->last_ref_pwr_lvl = 0;
+
+	/* Initialize ranging header using helper functions */
+	memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
+	/* Initialize segment accumulator */
+	t->segment_data.iov_base = NULL;
+	t->segment_data.iov_len = 0;
 }
 
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
@@ -1698,6 +1779,42 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
 		cont->step_data);
 }
 
+static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result_cont *cont,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+}
+
+static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->reqtracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+
+}
+
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
@@ -1707,7 +1824,12 @@ void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
 
-	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result_cont(rap, cont,
+								length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
@@ -1720,7 +1842,11 @@ void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 	DBG(rap, "Received CS subevent: len=%d", length);
 
 	/* Populate CsProcedureData and send RAS payload */
-	form_ras_data_with_cs_subevent_result(rap, data, length);
+	if (rap->resptracker->role == CS_REFLECTOR)
+		form_ras_data_with_cs_subevent_result(rap, data, length);
+
+	if (rap->reqtracker->role == CS_INITIATOR)
+		fill_initiator_data_from_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
@@ -1763,6 +1889,7 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 
 	if (!rap)
 		return;
@@ -1781,6 +1908,19 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	resptracker->config_id = data->config_id;
 	resptracker->role = data->role;
 	resptracker->rtt_type = data->rtt_type;
+
+	if (!rap->reqtracker) {
+		reqtracker = new0(struct cstracker, 1);
+		cs_tracker_init(reqtracker);
+		rap->reqtracker = reqtracker;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	/* Basic fields */
+	reqtracker->config_id = data->config_id;
+	reqtracker->role = data->role;
+	reqtracker->rtt_type = data->rtt_type;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -1815,6 +1955,694 @@ done:
 	return rap;
 }
 
+static void ras_pending_destroy(void *data)
+{
+	struct bt_rap_pending *pending = data;
+	struct bt_rap *rap = pending->rap;
+
+	if (queue_remove_if(rap->pending, NULL, pending))
+		free(pending);
+}
+
+static void ras_pending_complete(bool success, uint8_t att_ecode,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct bt_rap_pending *pending = user_data;
+
+	if (pending->func)
+		pending->func(pending->rap, success, att_ecode, value, length,
+					  pending->userdata);
+}
+
+static void rap_read_value(struct bt_rap *rap, uint16_t value_handle,
+			   rap_func_t func, void *user_data)
+{
+	struct bt_rap_pending *pending;
+
+	pending = new0(struct bt_rap_pending, 1);
+	pending->rap = rap;
+	pending->func = func;
+	pending->userdata = user_data;
+
+	pending->id = bt_gatt_client_read_value(rap->client, value_handle,
+						ras_pending_complete, pending,
+						ras_pending_destroy);
+	if (!pending->id) {
+		DBG(rap, "Unable to send Read request");
+		free(pending);
+		return;
+	}
+
+	queue_push_tail(rap->pending, pending);
+}
+
+static void ras_register(uint16_t att_ecode, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (att_ecode)
+		DBG(notify->rap, "RAS register failed 0x%04x", att_ecode);
+
+	(void)notify;
+}
+
+static void rap_notify(uint16_t value_handle, const uint8_t *value,
+				uint16_t length, void *user_data)
+{
+	struct bt_rap_notify *notify = user_data;
+
+	if (notify->func)
+		notify->func(notify->rap, value_handle, value, length,
+					 notify->user_data);
+}
+
+static void rap_notify_destroy(void *data)
+{
+	struct bt_rap_notify *notify = data;
+	struct bt_rap *rap = notify->rap;
+
+	if (queue_remove_if(rap->notify, NULL, notify))
+		free(notify);
+}
+
+static unsigned int bt_rap_register_notify(struct bt_rap *rap,
+					uint16_t value_handle,
+					rap_notify_t func,
+					void *user_data)
+{
+	struct bt_rap_notify *notify;
+
+	notify = new0(struct bt_rap_notify, 1);
+	notify->rap = rap;
+	notify->func = func;
+	notify->user_data = user_data;
+
+	DBG(rap, "register for notifications");
+
+	notify->id = bt_gatt_client_register_notify(rap->client,
+					value_handle, ras_register,
+					rap_notify, notify,
+					rap_notify_destroy);
+	if (!notify->id) {
+		DBG(rap, "Unable to register for notifications");
+		free(notify);
+		return 0;
+	}
+
+	queue_push_tail(rap->notify, notify);
+
+	return notify->id;
+}
+
+static inline bool parse_segmentation_header(struct iovec *iov,
+					     struct segmentation_header *s)
+{
+	uint8_t byte;
+
+	if (!util_iov_pull_u8(iov, &byte))
+		return false;
+
+	s->first_segment = (byte & 0x01) ? 1 : 0;
+	s->last_segment = (byte & 0x02) ? 1 : 0;
+	s->rolling_segment_counter = (byte >> 2) & 0x3F;
+
+	return true;
+}
+
+static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
+				int16_t *q_sample)
+{
+	uint32_t buffer;
+	uint32_t i12;
+	uint32_t q12;
+
+	if (!util_iov_pull_le24(iov, &buffer)) {
+		*i_sample = 0;
+		*q_sample = 0;
+		return;
+	}
+
+	i12 =  buffer        & 0x0FFFU;   /* bits 0..11 */
+	q12 = (buffer >> 12) & 0x0FFFU;   /* bits 12..23 */
+
+	*i_sample = SIGN_EXTEND_TO_16(i12, 12);
+	*q_sample = SIGN_EXTEND_TO_16(q12, 12);
+}
+
+static size_t get_mode_zero_length(enum cs_role remote_role)
+{
+	return (remote_role == CS_ROLE_INITIATOR) ?
+		CS_MODE_ZERO_WIRE_INIT_SIZE :
+		CS_MODE_ZERO_WIRE_REF_SIZE;
+}
+
+static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
+			    enum cs_role remote_role)
+{
+	uint8_t packet_quality;
+	int8_t packet_rssi_dbm;
+	uint8_t packet_ant;
+	uint32_t init_measured_freq_offset = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 0: failed to parse common fields");
+		return;
+	}
+
+	if (remote_role == CS_ROLE_INITIATOR) {
+		if (!util_iov_pull_le32(mode_iov, &init_measured_freq_offset)) {
+			DBG(rap, "Mode 0: failed to parse freq offset");
+			return;
+		}
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_one_length(bool include_pct)
+{
+	if (include_pct)
+		return CS_MODE_ONE_WIRE_SIZE_MAX;
+	return CS_MODE_ONE_WIRE_SIZE_MIN;
+}
+
+static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
+			   enum cs_role remote_role, bool include_pct)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 1: failed to parse fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_two_length(uint8_t num_antenna_paths)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+
+	return 1 + (4 * num_tone_data);
+}
+
+static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
+			   uint8_t num_antenna_paths)
+{
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 2: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 2: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
+		ant_perm_index);
+
+	/*Store this data as reflecter data*/
+}
+
+static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
+{
+	uint8_t num_tone_data = num_antenna_paths + 1;
+	size_t len;
+
+	len = 6;
+	if (include_pct)
+		len += 6;
+
+	len += 1 + (4 * num_tone_data);
+
+	return len;
+}
+
+static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
+			     enum cs_role remote_role, bool include_pct,
+			     uint8_t num_antenna_paths)
+{
+	uint8_t packet_quality;
+	uint8_t packet_nadm;
+	int8_t packet_rssi_dbm;
+	int16_t time_value;
+	uint8_t packet_ant;
+	int16_t pct1_i = 0, pct1_q = 0;
+	int16_t pct2_i = 0, pct2_q = 0;
+	uint8_t ant_perm_index;
+	int16_t tone_pct_i[5];
+	int16_t tone_pct_q[5];
+	uint8_t tone_quality[5];
+	uint8_t k;
+	uint8_t num_paths = (num_antenna_paths + 1) < 5 ?
+				(num_antenna_paths + 1) : 5;
+
+	if (!util_iov_pull_u8(mode_iov, &packet_quality) ||
+	    !util_iov_pull_u8(mode_iov, &packet_nadm) ||
+	    !util_iov_pull_u8(mode_iov, (uint8_t *)&packet_rssi_dbm) ||
+	    !util_iov_pull_le16(mode_iov, (uint16_t *)&time_value) ||
+	    !util_iov_pull_u8(mode_iov, &packet_ant)) {
+		DBG(rap, "Mode 3: failed to parse Mode 1 fixed fields");
+		return;
+	}
+
+	if (include_pct) {
+		parse_i_q_sample(mode_iov, &pct1_i, &pct1_q);
+		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
+	}
+
+	if (!util_iov_pull_u8(mode_iov, &ant_perm_index)) {
+		DBG(rap, "Mode 3: failed to parse ant_perm_index");
+		return;
+	}
+
+	for (k = 0; k < num_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (mode_iov->iov_len < 4) {
+			DBG(rap, "Mode 3: insufficient PCT for "
+				"path %u (rem=%zu)",
+				k, mode_iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(mode_iov, &i_val, &q_val);
+		tone_pct_i[k] = i_val;
+		tone_pct_q[k] = q_val;
+
+		util_iov_pull_u8(mode_iov, &tone_quality[k]);
+		DBG(rap, "tone_quality_indicator : %d",
+			tone_quality[k]);
+		DBG(rap, "[i, q] : %d, %d",
+			tone_pct_i[k],
+			tone_pct_q[k]);
+	}
+
+	/*Store this data as reflecter data*/
+}
+
+static bool parse_subevent_header(struct iovec *iov,
+					struct ras_subevent_header *hdr)
+{
+	uint16_t start_acl, freq_comp;
+	uint8_t done_byte, abort_byte, ref_pwr, num_steps;
+
+	if (!util_iov_pull_le16(iov, &start_acl) ||
+	    !util_iov_pull_le16(iov, &freq_comp) ||
+	    !util_iov_pull_u8(iov, &done_byte) ||
+	    !util_iov_pull_u8(iov, &abort_byte) ||
+	    !util_iov_pull_u8(iov, &ref_pwr) ||
+	    !util_iov_pull_u8(iov, &num_steps))
+		return false;
+
+	hdr->start_acl_conn_event = start_acl;
+	hdr->frequency_compensation = freq_comp;
+	hdr->ranging_done_status = RAS_DONE_STATUS_UNPACK_RANGING(done_byte);
+	hdr->subevent_done_status = RAS_DONE_STATUS_UNPACK_SUBEVENT(done_byte);
+	hdr->ranging_abort_reason =
+		RAS_ABORT_REASON_UNPACK_RANGING(abort_byte);
+	hdr->subevent_abort_reason =
+		RAS_ABORT_REASON_UNPACK_SUBEVENT(abort_byte);
+	hdr->reference_power_level = (int8_t)ref_pwr;
+	hdr->num_steps_reported = num_steps;
+
+	return true;
+}
+
+static bool parse_step(struct bt_rap *rap, struct iovec *iov,
+			struct cstracker *reqtracker,
+			uint8_t num_antenna_paths, uint8_t step_idx)
+{
+	uint8_t mode_byte, step_mode;
+	bool include_pct;
+	enum cs_role remote_role;
+	size_t step_payload_len;
+	struct iovec mode_iov;
+	void *payload;
+
+	if (!util_iov_pull_u8(iov, &mode_byte)) {
+		DBG(rap, "Insufficient data for step %u", step_idx);
+		return false;
+	}
+
+	if (mode_byte & RAS_STEP_ABORTED_BIT) {
+		DBG(rap, "  Step %u: mode=%u (aborted)",
+			step_idx, mode_byte & 0x7F);
+		return true;
+	}
+
+	step_mode = mode_byte & 0x7F;
+	include_pct = (reqtracker->rtt_type == 0x01 ||
+			reqtracker->rtt_type == 0x02);
+	remote_role = (reqtracker->role == CS_ROLE_INITIATOR) ?
+			CS_ROLE_REFLECTOR : CS_ROLE_INITIATOR;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		step_payload_len = get_mode_zero_length(remote_role);
+		break;
+	case CS_MODE_ONE:
+		step_payload_len = get_mode_one_length(include_pct);
+		break;
+	case CS_MODE_TWO:
+		step_payload_len = get_mode_two_length(num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		step_payload_len = get_mode_three_length(num_antenna_paths,
+								include_pct);
+		break;
+	default:
+		DBG(rap, "  Step %u: unknown mode=%u", step_idx, step_mode);
+		return true;
+	}
+
+	DBG(rap, "  Step %u: mode=%u payload_len=%zu",
+	    step_idx, step_mode, step_payload_len);
+
+	payload = util_iov_pull(iov, step_payload_len);
+	if (!payload) {
+		DBG(rap, "Insufficient data for step %u payload "
+			"(need %zu, have %zu)",
+			step_idx, step_payload_len, iov->iov_len);
+		return false;
+	}
+
+	mode_iov.iov_base = payload;
+	mode_iov.iov_len = step_payload_len;
+
+	switch (step_mode) {
+	case CS_MODE_ZERO:
+		parse_mode_zero(rap, &mode_iov, remote_role);
+		break;
+	case CS_MODE_ONE:
+		parse_mode_one(rap, &mode_iov, remote_role, include_pct);
+		break;
+	case CS_MODE_TWO:
+		parse_mode_two(rap, &mode_iov, num_antenna_paths);
+		break;
+	case CS_MODE_THREE:
+		parse_mode_three(rap, &mode_iov, remote_role, include_pct,
+						num_antenna_paths);
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
+				struct cstracker *reqtracker,
+				uint8_t num_antenna_paths, uint8_t num_steps)
+{
+	uint8_t i;
+
+	for (i = 0; i < num_steps; i++) {
+		if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
+			break;
+	}
+}
+
+static void parse_ras_data_segments(struct bt_rap *rap,
+			       struct cstracker *reqtracker)
+{
+	struct iovec iov;
+	uint8_t antenna_mask;
+	uint8_t num_antenna_paths;
+
+	if (!rap || !reqtracker)
+		return;
+
+	DBG(rap, "Complete RAS data received: %zu bytes",
+	    reqtracker->segment_data.iov_len);
+
+	antenna_mask =
+		ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
+	num_antenna_paths = antenna_mask_count_paths(antenna_mask);
+
+	iov = reqtracker->segment_data;
+
+	while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
+		struct ras_subevent_header hdr;
+
+		if (!parse_subevent_header(&iov, &hdr))
+			break;
+
+		DBG(rap, "Parsed subevent: start_acl=%u "
+			"freq_comp=%d ref_pwr=%d steps=%u",
+		    hdr.start_acl_conn_event,
+		    hdr.frequency_compensation,
+		    hdr.reference_power_level,
+		    hdr.num_steps_reported);
+
+		parse_subevent_steps(rap, &iov, reqtracker,
+					num_antenna_paths,
+					hdr.num_steps_reported);
+
+		if (hdr.subevent_done_status ==
+		    SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
+		    hdr.ranging_done_status ==
+		    RANGING_DONE_ALL_RESULTS_COMPLETE) {
+			DBG(rap, "Ranging procedure complete");
+			break;
+		}
+	}
+
+	free(reqtracker->segment_data.iov_base);
+	reqtracker->segment_data.iov_base = NULL;
+	reqtracker->segment_data.iov_len = 0;
+}
+
+static bool process_first_segment(struct bt_rap *rap,
+					struct cstracker *reqtracker,
+					struct iovec *iov, bool last_segment)
+{
+	uint16_t counter_config_val;
+	int8_t selected_tx_power;
+	uint8_t antenna_pct;
+
+	if (!util_iov_pull_le16(iov, &counter_config_val) ||
+	    !util_iov_pull_u8(iov, (uint8_t *)&selected_tx_power) ||
+	    !util_iov_pull_u8(iov, &antenna_pct)) {
+		DBG(rap, "First segment too short for ranging header");
+		return false;
+	}
+
+	ranging_header_set_counter(&reqtracker->ranging_header_,
+				counter_config_val & 0x0FFF);
+	ranging_header_set_config_id(&reqtracker->ranging_header_,
+				(counter_config_val >> 12) & 0x0F);
+	reqtracker->ranging_header_.selected_tx_power = selected_tx_power;
+	reqtracker->ranging_header_.antenna_pct = antenna_pct;
+
+	DBG(rap, "First segment: parsed ranging header "
+	    "(counter=%u, config_id=%u, tx_pwr=%d)",
+	    counter_config_val & 0x0FFF,
+	    (counter_config_val >> 12) & 0x0F,
+	    selected_tx_power);
+
+	if (reqtracker->segment_data.iov_base) {
+		free(reqtracker->segment_data.iov_base);
+		reqtracker->segment_data.iov_base = NULL;
+		reqtracker->segment_data.iov_len = 0;
+	}
+
+	if (iov->iov_len > 0) {
+		if (!util_iov_append(&reqtracker->segment_data,
+					iov->iov_base, iov->iov_len)) {
+			DBG(rap, "Failed to initialize segment accumulator");
+			return false;
+		}
+		DBG(rap, "First segment: initialized accumulator "
+		    "with %zu bytes", iov->iov_len);
+	}
+
+	if (!last_segment)
+		return false;
+
+	DBG(rap, "Single-segment: first=1, last=1");
+	return true;
+}
+
+static void ras_realtime_notify_cb(struct bt_rap *rap, uint16_t value_handle,
+				   const uint8_t *value, uint16_t length,
+				   void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	struct segmentation_header seg_hdr;
+	struct cstracker *reqtracker;
+
+	if (!rap || !value || length == 0) {
+		DBG(rap, "Invalid notification data");
+		return;
+	}
+
+	DBG(rap, "Received real-time notification: handle=0x%04x len=%u",
+	    value_handle, length);
+
+	if (!parse_segmentation_header(&iov, &seg_hdr)) {
+		DBG(rap, "Failed to parse segmentation header");
+		return;
+	}
+
+	DBG(rap, "Segment: first=%u last=%u counter=%u",
+	    seg_hdr.first_segment, seg_hdr.last_segment,
+	    seg_hdr.rolling_segment_counter);
+
+	if (!rap->reqtracker) {
+		DBG(rap, "reqtracker is not initialised");
+		return;
+	}
+
+	reqtracker = rap->reqtracker;
+
+	if (seg_hdr.first_segment) {
+		if (!process_first_segment(rap, reqtracker, &iov,
+						seg_hdr.last_segment))
+			return;
+	} else {
+		if (iov.iov_len > 0) {
+			if (!util_iov_append(&reqtracker->segment_data,
+						iov.iov_base, iov.iov_len)) {
+				DBG(rap, "Failed to append segment data");
+				return;
+			}
+			DBG(rap, "Continuation segment: appended "
+			    "%zu bytes (total=%zu)",
+			    iov.iov_len,
+			    reqtracker->segment_data.iov_len);
+		}
+	}
+
+	/* Last segment: parse complete RAS data */
+	if (seg_hdr.last_segment)
+		parse_ras_data_segments(rap, reqtracker);
+}
+
+static void read_ras_features(struct bt_rap *rap, bool success,
+			uint8_t att_ecode,
+			const uint8_t *value, uint16_t length,
+			void *user_data)
+{
+	struct iovec iov = { .iov_base = (void *)value, .iov_len = length };
+	uint32_t features = 0;
+	struct ras *ras;
+	bool supports_realtime;
+	bool retrieve_lost;
+	bool abort_operation;
+
+	if (!success) {
+		DBG(rap, "Unable to read RAS Features: error 0x%02x",
+			att_ecode);
+		return;
+	}
+
+	ras = rap_get_ras(rap);
+
+	if (length == 0 || length > 4) {
+		DBG(rap, "Invalid RAS Features length: %u (expected 1-4)",
+			length);
+		return;
+	}
+
+	if (length < 4) {
+		uint8_t padded[4] = { 0, 0, 0, 0 };
+
+		memcpy(padded, value, length);
+		iov.iov_base = padded;
+		iov.iov_len = 4;
+		DBG(rap, "RAS Features: short read (%u bytes), zero-pad to 4",
+		    length);
+	}
+
+	if (!util_iov_pull_le32(&iov, &features)) {
+		DBG(rap, "Unable to parse RAS Features value");
+		return;
+	}
+
+	DBG(rap, "RAS Features: 0x%08x", features);
+
+	supports_realtime = (features & 0x01) != 0;
+	retrieve_lost = (features & 0x02) != 0;
+	abort_operation = (features & 0x04) != 0;
+
+	DBG(rap, "RAS Features - Real-time: %s, Retrieve Lost: %s, Abort: %s",
+	    supports_realtime ? "Yes" : "No",
+	    retrieve_lost ? "Yes" : "No",
+	    abort_operation ? "Yes" : "No");
+
+	DBG(rap, "RAS features read successfully, capabilities determined");
+
+	/* Register for real-time characteristic notifications if supported */
+	if (supports_realtime && ras && ras->realtime_chrc && rap->client) {
+		uint16_t value_handle;
+		bt_uuid_t uuid;
+
+		if (gatt_db_attribute_get_char_data(ras->realtime_chrc,
+					NULL, &value_handle,
+					NULL, NULL, &uuid)) {
+			unsigned int notify_id;
+
+			notify_id = bt_rap_register_notify(rap,
+						value_handle,
+						ras_realtime_notify_cb,
+						NULL);
+			if (!notify_id)
+				DBG(rap, "Failed to register for "
+					"real-time notifications");
+			else
+				DBG(rap, "Registered for real-time "
+					"features: id=%u", notify_id);
+		}
+	} else {
+		DBG(rap, "On demand ranging "
+			"remote device - skipping notification "
+			"registration");
+	}
+}
+
 static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct bt_rap *rap = user_data;
@@ -1848,6 +2676,10 @@ static void foreach_rap_char(struct gatt_db_attribute *attr, void *user_data)
 			return;
 
 		ras->feat_chrc = attr;
+		if (rap->client) {
+			rap_read_value(rap, value_handle,
+					read_ras_features, rap);
+		}
 	}
 
 	if (!bt_uuid_cmp(&uuid, &uuid_realtime)) {
-- 
2.34.1


^ permalink raw reply related

* [bluez/bluez] 090000: shared/rap: Add client ranging registration and no...
From: prathibhamadugonde @ 2026-05-21 11:55 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098658
  Home:   https://github.com/bluez/bluez
  Commit: 090000d083d1fd56fd3379640ba8dca983b05e23
      https://github.com/bluez/bluez/commit/090000d083d1fd56fd3379640ba8dca983b05e23
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M src/shared/rap.c

  Log Message:
  -----------
  shared/rap: Add client ranging registration and notification parsing

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper functions for readablitiy and code indentation.



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

^ permalink raw reply

* [bluez/bluez] 53ac4e: shared/rap: Add client ranging registration and no...
From: prathibhamadugonde @ 2026-05-21 11:55 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098678
  Home:   https://github.com/bluez/bluez
  Commit: 53ac4ed034f11324a3d7c6f4daaa2f72e46e3fe5
      https://github.com/bluez/bluez/commit/53ac4ed034f11324a3d7c6f4daaa2f72e46e3fe5
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-05-21 (Thu, 21 May 2026)

  Changed paths:
    M src/shared/rap.c

  Log Message:
  -----------
  shared/rap: Add client ranging registration and notification parsing

Read the RAS Features characteristic to determine whether the remote
device supports real-time ranging. If supported, register for real-time
characteristic notifications using the reqtracker for the CS initiator
role.

Parse incoming segmented RAS ranging data notifications by accumulating
segments via iovec and parsing complete subevent headers and CS mode 0-3
step data, including IQ/tone PCT samples, once the last segment arrives.

Changes in v2:
Removed unneccsary DBG logs and unused variables.
Use helper functions for readablitiy and code indentation.



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-05-21 11:55 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1098497
  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

* [syzbot] [bluetooth?] WARNING in hci_send_acl (4)
From: syzbot @ 2026-05-21 12:20 UTC (permalink / raw)
  To: linux-bluetooth, linux-kernel, luiz.dentz, marcel, syzkaller-bugs

Hello,

syzbot found the following issue on:

HEAD commit:    5cbb61bf4168 arm64/fpsimd: ptrace: zero target's fpsimd_st..
git tree:       git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci
console output: https://syzkaller.appspot.com/x/log.txt?x=138bd02e580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=a834c6344141a58b
dashboard link: https://syzkaller.appspot.com/bug?extid=0e3882021d9c3ae26430
compiler:       Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
userspace arch: arm64

Unfortunately, I don't have any reproducer for this issue yet.

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/04156ec16593/disk-5cbb61bf.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/6bfa041e2c79/vmlinux-5cbb61bf.xz
kernel image: https://storage.googleapis.com/syzbot-assets/a92d82d8a79e/Image-5cbb61bf.gz.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+0e3882021d9c3ae26430@syzkaller.appspotmail.com

------------[ cut here ]------------
workqueue: cannot queue hci_tx_work on wq hci2
WARNING: kernel/workqueue.c:2298 at __queue_work+0xf6c/0x12f8 kernel/workqueue.c:2296, CPU#0: kbnepd \x01/10362
Modules linked in:
CPU: 0 UID: 0 PID: 10362 Comm: kbnepd \x01 Tainted: G             L      syzkaller #0 PREEMPT 
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026
pstate: 634000c5 (nZCv daIF +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : __queue_work+0xf6c/0x12f8 kernel/workqueue.c:2296
lr : __queue_work+0xf6c/0x12f8 kernel/workqueue.c:2296
sp : ffff8000971a7560
x29: ffff8000971a75a0 x28: ffff0000ed3e98c0 x27: ffff0000ed3e9990
x26: ffff0000ca68f800 x25: 0000000000000008 x24: ffff0000ca68f9c0
x23: dfff800000000000 x22: ffff0000d8bb002c x21: 0000000000208040
x20: ffff800089f06000 x19: ffff0000ce2d0c50 x18: 1fffe00035c20e20
x17: ffff8000888db000 x16: ffff80008898cfc0 x15: ffff0001ae10710c
x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000000
x11: 0000000000001c1f x10: 0000000000ff0100 x9 : 2106f328601d6d00
x8 : 2106f328601d6d00 x7 : ffff8000804886d0 x6 : 0000000000000000
x5 : 0000000000000001 x4 : 0000000000000000 x3 : ffff8000802f13b0
x2 : 0000000100000000 x1 : ffff0000d8bb0000 x0 : 0000000000000000
Call trace:
 __queue_work+0xf6c/0x12f8 kernel/workqueue.c:2296 (P)
 queue_work_on+0xc0/0x130 kernel/workqueue.c:2432
 queue_work include/linux/workqueue.h:696 [inline]
 hci_send_acl+0x4a4/0x7c4 net/bluetooth/hci_core.c:3283
 l2cap_do_send+0x18c/0x2b4 net/bluetooth/l2cap_core.c:1019
 l2cap_chan_send+0x108c/0x1dc4 net/bluetooth/l2cap_core.c:-1
 l2cap_sock_sendmsg+0x284/0x428 net/bluetooth/l2cap_sock.c:1164
 sock_sendmsg_nosec net/socket.c:787 [inline]
 __sock_sendmsg+0xc8/0x138 net/socket.c:802
 sock_sendmsg+0x154/0x228 net/socket.c:825
 kernel_sendmsg+0x54/0x6c net/socket.c:849
 bnep_tx_frame net/bluetooth/bnep/core.c:466 [inline]
 bnep_session+0x1ce0/0x2298 net/bluetooth/bnep/core.c:509
 kthread+0x2f0/0x3c0 kernel/kthread.c:436
 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:842
irq event stamp: 284
hardirqs last  enabled at (283): [<ffff8000803bd554>] raw_spin_rq_unlock_irq kernel/sched/sched.h:1644 [inline]
hardirqs last  enabled at (283): [<ffff8000803bd554>] finish_lock_switch+0x160/0x204 kernel/sched/core.c:5124
hardirqs last disabled at (284): [<ffff80008034c854>] queue_work_on+0x4c/0x130 kernel/workqueue.c:2428
softirqs last  enabled at (236): [<ffff800084ab928c>] local_bh_enable+0x10/0x34 include/linux/bottom_half.h:32
softirqs last disabled at (232): [<ffff800084ab9258>] local_bh_disable+0x10/0x34 include/linux/bottom_half.h:19
---[ end trace 0000000000000000 ]---


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

^ permalink raw reply

* RE: Bluetooth: hci_conn: Fix memory leak in hci_le_big_terminate()
From: bluez.test.bot @ 2026-05-21 12:23 UTC (permalink / raw)
  To: linux-bluetooth, jhapavitra98
In-Reply-To: <20260521080414.44460-1-jhapavitra98@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1482 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=1098541

---Test result---

Test Summary:
CheckPatch                    PASS      0.75 seconds
GitLint                       PASS      0.34 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      27.54 seconds
CheckAllWarning               PASS      29.95 seconds
CheckSparse                   PASS      28.59 seconds
BuildKernel32                 PASS      26.38 seconds
TestRunnerSetup               PASS      589.00 seconds
TestRunner_l2cap-tester       PASS      379.35 seconds
TestRunner_iso-tester         PASS      597.75 seconds
TestRunner_bnep-tester        PASS      19.30 seconds
TestRunner_mgmt-tester        PASS      2024.30 seconds
TestRunner_rfcomm-tester      PASS      63.92 seconds
TestRunner_sco-tester         PASS      142.85 seconds
TestRunner_ioctl-tester       PASS      134.60 seconds
TestRunner_mesh-tester        PASS      60.19 seconds
TestRunner_smp-tester         PASS      18.14 seconds
TestRunner_userchan-tester    PASS      19.35 seconds
TestRunner_6lowpan-tester     PASS      51.23 seconds
IncrementalBuild              PASS      25.94 seconds



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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: [BlueZ,v2] shared/rap: Add client ranging registration and notification parsing
From: bluez.test.bot @ 2026-05-21 12:47 UTC (permalink / raw)
  To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260521103227.2479443-1-prathm@qti.qualcomm.com>

[-- Attachment #1: Type: text/plain, Size: 1338 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=1098658

---Test result---

Test Summary:
CheckPatch                    PASS      1.00 seconds
GitLint                       PASS      0.34 seconds
BuildEll                      PASS      19.90 seconds
BluezMake                     PASS      601.40 seconds
MakeCheck                     PASS      0.99 seconds
MakeDistcheck                 FAIL      216.80 seconds
CheckValgrind                 PASS      202.65 seconds
CheckSmatch                   PASS      323.03 seconds
bluezmakeextell               PASS      165.37 seconds
IncrementalBuild              PASS      604.56 seconds
ScanBuild                     PASS      918.98 seconds

Details
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:

make[4]: *** [Makefile:10239: test-suite.log] Error 1
make[3]: *** [Makefile:10347: check-TESTS] Error 2
make[2]: *** [Makefile:10818: check-am] Error 2
make[1]: *** [Makefile:10820: check] Error 2
make: *** [Makefile:10741: distcheck] Error 1


https://github.com/bluez/bluez/pull/2142

---
Regards,
Linux Bluetooth


^ permalink raw reply


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