* Re: [PATCH BlueZ 0/3] client/btpclient: Add GAP extended advertising support
From: patchwork-bot+bluetooth @ 2026-05-21 13:30 UTC (permalink / raw)
To: =?utf-8?b?RnLDqWTDqXJpYyBEYW5pcyA8ZnJlZGVyaWMuZGFuaXNAY29sbGFib3JhLmNvbT4=?=
Cc: linux-bluetooth
In-Reply-To: <20260519105519.226648-1-frederic.danis@collabora.com>
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Tue, 19 May 2026 12:55:16 +0200 you wrote:
> The BTP_OP_GAP_SET_EXTENDED_ADVERTISING command is used by at least the
> BAP/USR/DISC/BV-01-C test.
>
> Since this opcode is defined as 0x21 (33), it does not fit into the
> current fixed-size command bitmap. This series refactors the way
> btpclient reports supported auto-PTS commands so it can grow the bitmap
> dynamically as needed.
>
> [...]
Here is the summary with links:
- [BlueZ,1/3] client/btpclient: refactor read-commands bitmap building
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=0e55974d713b
- [BlueZ,2/3] client/btpclient: Replace advertising defines by shared ones
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=9074d38c5fed
- [BlueZ,3/3] client/btpclient: Add BTP_OP_GAP_SET_EXTENDED_ADVERTISING support
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=588a0267d8fa
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* RE: [BlueZ,v4] shared/rap: Add client ranging registration and notification parsing
From: bluez.test.bot @ 2026-05-21 12:52 UTC (permalink / raw)
To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260521111550.2508891-1-prathm@qti.qualcomm.com>
[-- Attachment #1: Type: text/plain, Size: 1339 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=1098678
---Test result---
Test Summary:
CheckPatch PASS 0.80 seconds
GitLint PASS 0.20 seconds
BuildEll PASS 20.27 seconds
BluezMake PASS 660.63 seconds
MakeCheck PASS 0.93 seconds
MakeDistcheck FAIL 235.21 seconds
CheckValgrind PASS 221.51 seconds
CheckSmatch PASS 347.96 seconds
bluezmakeextell PASS 181.11 seconds
IncrementalBuild PASS 650.01 seconds
ScanBuild PASS 1020.17 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/2143
---
Regards,
Linux Bluetooth
^ permalink raw reply
* Re: [PATCH v3] Bluetooth: L2CAP: reject BR/EDR signaling packets over MTUsig
From: Luiz Augusto von Dentz @ 2026-05-21 12:51 UTC (permalink / raw)
To: Michael Bommarito
Cc: Marcel Holtmann, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-bluetooth, netdev, linux-kernel,
stable
In-Reply-To: <20260521000555.3712030-1-michael.bommarito@gmail.com>
Hi Michael,
On Wed, May 20, 2026 at 8:06 PM Michael Bommarito
<michael.bommarito@gmail.com> wrote:
>
> net/bluetooth/l2cap_core.c:l2cap_sig_channel() accepts BR/EDR
> signaling packets up to the channel MTU and dispatches each command
> without enforcing the signaling MTU (MTUsig). A Bluetooth BR/EDR peer
> within radio range can send a fixed-channel CID 0x0001 packet that is
> larger than MTUsig and contains many L2CAP_ECHO_REQ commands before
> pairing. In a real-radio stock-kernel run, one 681-byte signaling
> packet containing 168 zero-length ECHO_REQ commands made the target
> transmit 168 ECHO_RSP frames over about 220 ms.
>
> Impact: a Bluetooth BR/EDR peer within radio range, before pairing, can
> force 168 ECHO_RSP frames from one 681-byte fixed-channel signaling
> packet containing packed ECHO_REQ commands.
>
> Define Linux's BR/EDR signaling MTU as the spec minimum of 48 bytes and
> reject any larger signaling packet with one L2CAP_COMMAND_REJECT_RSP
> carrying L2CAP_REJ_MTU_EXCEEDED before any command is dispatched.
>
> The Bluetooth Core spec wording for MTUExceeded says the reject
> identifier shall match the first request command in the packet, and
> that packets containing only responses shall be silently discarded.
> Linux intentionally deviates from that prescription: silently
> discarding desynchronizes the peer because the remote stack never
> learns its responses were dropped, and locating the first request
> command requires walking command headers past MTUsig, i.e. processing
> bytes from a packet we have already decided is too large to process.
> We therefore always emit one reject and use the identifier from the
> first command header (a single fixed-offset byte read), falling back
> to zero when the packet is too short to carry a header at all.
>
> The unrestricted BR/EDR signaling parser and ECHO_REQ response path both
> trace to the initial git import; no later introducing commit is
> available for a Fixes tag.
>
> Cc: stable@vger.kernel.org
> Suggested-by: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Link: https://lore.kernel.org/r/20260518002800.1361430-1-michael.bommarito@gmail.com
> Link: https://lore.kernel.org/r/20260520135034.1060859-1-michael.bommarito@gmail.com
> Assisted-by: Claude:claude-opus-4-7
> Assisted-by: Codex:gpt-5-5-xhigh
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
> I reproduced the stock behavior with a real-radio BR/EDR ACL link and a
> harness that sends a single fixed-channel signaling packet containing
> packed zero-length ECHO_REQ commands, and confirmed on a patched kernel
> that the same packet now produces one L2CAP_REJ_MTU_EXCEEDED command
> reject and zero ECHO_RSP frames. The patched code builds for
> net/bluetooth/l2cap_core.o on x86_64 defconfig with W=1. There are no
> in-tree Bluetooth selftests that reference l2cap_sig_channel(),
> L2CAP_SIG_MTU, or L2CAP_ECHO_REQ.
>
> Changes in v3:
> - Drop l2cap_sig_cmd_is_req() and l2cap_sig_first_req_ident(); the
> reject is now unconditional and uses only the first command
> header's identifier byte at a fixed offset. Per Luiz, the spec's
> "match the first request command identifier" rule would require
> parsing past MTUsig, and the spec's "silently discard if only
> responses" rule desynchronizes the peer.
> - Replace the v2 walk with a verbose comment quoting the relevant
> Bluetooth Core section and documenting why Linux deviates.
>
> Changes in v2:
> - Replace the per-PDU echo-count cap with the MTUsig direction from
> review.
> - Reject the whole over-MTUsig signaling packet with one
> L2CAP_REJ_MTU_EXCEEDED command reject.
> - Add L2CAP_SIG_MTU and drop over-MTUsig packets when no valid request
> command identifier is found.
>
> v1: https://lore.kernel.org/r/20260518002800.1361430-1-michael.bommarito@gmail.com
> v2: https://lore.kernel.org/r/20260520135034.1060859-1-michael.bommarito@gmail.com
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/l2cap_core.c | 47 +++++++++++++++++++++++++++++++++++
> 2 files changed, 48 insertions(+)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 5172afee54943..e0a1f2293679a 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -33,6 +33,7 @@
> /* L2CAP defaults */
> #define L2CAP_DEFAULT_MTU 672
> #define L2CAP_DEFAULT_MIN_MTU 48
> +#define L2CAP_SIG_MTU 48 /* BR/EDR signaling MTU */
> #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
> #define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF
> #define L2CAP_DEFAULT_TX_WINDOW 63
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 7701528f11677..0b1e062057695 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -5618,6 +5618,15 @@ static inline void l2cap_sig_send_rej(struct l2cap_conn *conn, u16 ident)
> l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
> }
>
> +static inline void l2cap_sig_send_mtu_rej(struct l2cap_conn *conn, u8 ident)
> +{
> + struct l2cap_cmd_rej_mtu rej;
> +
> + rej.reason = cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
> + rej.max_mtu = cpu_to_le16(L2CAP_SIG_MTU);
> + l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
> +}
> +
> static inline void l2cap_sig_channel(struct l2cap_conn *conn,
> struct sk_buff *skb)
> {
> @@ -5630,6 +5639,44 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
> if (hcon->type != ACL_LINK)
> goto drop;
>
> + /*
> + * Bluetooth Core v5.4, Vol 3, Part A, Section 4: the BR/EDR
> + * signaling channel has a fixed signaling MTU (MTUsig) whose
> + * minimum and default is 48 octets. Section 4.1 says that on
> + * an MTUExceeded command reject the identifier "shall match
> + * the first request command in the L2CAP packet" and that
> + * packets containing only response commands "shall be
> + * silently discarded".
> + *
> + * Linux intentionally deviates from that prescription:
> + *
> + * 1. Silently discarding desynchronizes the peer. The
> + * remote stack never learns its responses were dropped,
> + * so any state machine waiting on a paired response
> + * stalls until its own timer fires.
> + *
> + * 2. Locating "the first request command" requires walking
> + * command headers past MTUsig, i.e. processing bytes
> + * from a packet we have already decided is too large to
> + * process.
> + *
> + * Reject every over-MTUsig signaling packet with one
> + * L2CAP_REJ_MTU_EXCEEDED command reject. The reject's
> + * reason field is what tells the peer that the whole packet
> + * was discarded; the identifier value is informational, so
> + * we use the identifier from the first command header (a
> + * single fixed-offset byte read) or zero when the packet is
> + * too short to carry even one header.
> + */
> + if (skb->len > L2CAP_SIG_MTU) {
> + u8 ident = (skb->len >= L2CAP_CMD_HDR_SIZE) ?
> + skb->data[1] : 0;
Checking L2CAP_CMD_HDR_SIZE after L2CAP_SIG_MTU seems unnecessary, the
latter should always be large enough to accommodate a header.
> +
> + BT_DBG("signaling packet exceeds MTU");
I'd make it print skb->len and L2CAP_SIG_MTU e.g. %u > %u, skb->len,
L2CAP_SIG_MTU.
> + l2cap_sig_send_mtu_rej(conn, ident);
> + goto drop;
> + }
> +
> while (skb->len >= L2CAP_CMD_HDR_SIZE) {
> u16 len;
>
> --
> 2.53.0
>
--
Luiz Augusto von Dentz
^ 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
* 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
* [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
* [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
* [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] 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
* [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
* [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
* 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
* 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: [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
* [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
* [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
* [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
* [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
* 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
* 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
* [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
* [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 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
* 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] 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
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