* [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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox