Linux bluetooth development
 help / color / mirror / Atom feed
* xHCI TT: FS audio capture glitches when btusb SCO runs concurrently
From: Branislav Klocok @ 2026-07-18 10:18 UTC (permalink / raw)
  To: linux-usb; +Cc: linux-bluetooth, mathias.nyman

Hi,

I'd like to report (and ask for guidance on) an apparent conflict between
two concurrent full-speed isochronous streams on the same xHCI controller:
a full-speed USB audio device behind a hub (split transactions through the
hub's TT) and the SCO endpoints of an Intel Bluetooth controller (btusb)
on a root port.

Short version: while an HFP call is active over Bluetooth (SCO), the
capture stream of a full-speed USB headset plugged in *behind a dock hub*
is subtly corrupted. After mSBC encoding and the telephony path, the far
end hears severely "robotic" audio. Moving the same headset to a direct
root port makes the problem disappear immediately; moving it back behind
the dock brings it back. CVSD calls are perceptually unaffected.

Environment
-----------
- ThinkPad X1 Carbon 6th gen; xHCI: Intel Sunrise Point-LP [8086:9d2f]
- Kernel 7.1.3-1-default (openSUSE Tumbleweed), BlueZ 5.82,
   PipeWire 1.6.8 / WirePlumber 0.5.15 (HFP native backend, laptop = HF)
- Bluetooth: Intel 8265 [8087:0a2b], full-speed, internal (bus 1 root
   port 7), fw ibt-12-16
- USB headset: C-Media [0d8c:0014], full-speed, mono capture 48 kHz
- Dock: Lenovo USB dock (hubs 17ef:3071/17ef:3070, cascade incl.
   067b:2586 and 05e3:0610)
- HFP counterpart: Android phone (Volla X23) as AG; a call was placed to
   a second handset held at my ear, so I judged the far-end quality
   directly while speaking

SCO detail during mSBC: eSCO, air mode Transparent, 60-byte RX/TX packet
length, btusb isoc alt setting 1 (9-byte packets), HCI SCO frames
dlen 24. CVSD uses alt setting 2 (17-byte packets).

Reproduction (single call, changing only the capture source / port)
-------------------------------------------------------------------
I kept the same position and speech style throughout and judged the
quality on the far-end handset:

   uplink capture source                    far-end verdict
   1. HS webcam (behind the same dock)      clean
   2. FS headset behind dock hub A          severely "robotic"
   3. same, headset moved 30 cm away        severely "robotic"
                                            (rules out mic proximity/level)
   4. FS headset on a DIRECT root port      clean
   5. FS headset behind dock hub B          severely "robotic" again
                                            (immediately reversible)

A parallel capture of the same source during phase 5 (recording to a file
while the stream also feeds the call) contains audible subtle glitching
already at the capture side -- i.e. the corruption exists in the USB
capture data before any Bluetooth/mSBC processing. Amplitude analysis
shows no zero-runs or full-scale clicks, so the defect consists of small
sample-level discontinuities, which the frame-based mSBC codec then
amplifies into severe artifacts, while sample-based CVSD mostly masks
them.

What I ruled out
----------------
- Bluetooth host stack: btmon traces covering a clean -> robotic -> clean
   cycle show the HCI SCO TX stream is byte-perfect (intact 60-byte mSBC
   framing with H2 headers, ~3333 frames/10 s constant) and the write
   timing pattern is identical between clean and robotic phases (bursts of
   3x24 B every ~7.5 ms). The RX direction is likewise clean. So
   BlueZ/PipeWire/btusb submit correct data on time; the corruption
   happens elsewhere.
- Audio content/level: same speech, same position (phase 3).
- The specific adapter/hub: reproduced behind two different hub chains,
   and with a different FS combo headset (Sennheiser) behind a different
   (Thunderbolt) dock in another location -- there the mSBC *downlink*
   died completely while CVSD kept working.
- PipeWire graph effects: an *idle* parallel capture from the FS device
   behind the dock (both via PipeWire and via direct ALSA hw:) does NOT
   disturb a call whose uplink comes from the HS webcam -- the corruption
   only matters when the TT capture stream is the one feeding the call.
   Consistent with the corruption being on the TT capture stream itself.

Possibly related: "Bluetooth: hci0: corrupted SCO packet" bursts (~14-32
messages) at SCO setup time, with both codecs, transient. Earlier this
year, on 6.18.8 with the Thunderbolt dock setup, mSBC showed frequent
decode errors but audio still flowed; I have not bisected.

Questions
---------
1. Is this a known limitation of xHCI TT split-isochronous scheduling
    with two concurrent FS isoc streams (one behind a TT, one on a root
    port)? EHCI had a software TT scheduler
    (CONFIG_USB_EHCI_TT_NEWSCHED); as far as I can tell xHCI relies on
    hardware scheduling with no equivalent knob.
2. Which captures would help most? I can provide btmon traces (a 26 MB
    clean/robotic cycle), usbmon captures, xhci dynamic debug logs and
    audio samples, and I'm happy to test patches.

Best regards,

-- 
Branislav Klocok
email: branislav.klocok@orava.sk
tel: +421908703366


^ permalink raw reply

* Re: SIG-Qualification: Problems with setting TSPC_L2CAP_4_3
From: Chinedu Mmadu @ 2026-07-18  9:34 UTC (permalink / raw)
  To: ceggers; +Cc: frederic.danis, linux-bluetooth, luiz.von.dentz


Sent from my iPhone

^ permalink raw reply

* [bluez/bluez]
From: BluezTestBot @ 2026-07-18  4:17 UTC (permalink / raw)
  To: linux-bluetooth

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

* [bluetooth-next:master] BUILD SUCCESS bd8bee79e1fa8db2d587733f8b6fd9597b04d6e3
From: kernel test robot @ 2026-07-18  4:04 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: bd8bee79e1fa8db2d587733f8b6fd9597b04d6e3  Bluetooth: btintel_pcie: serialize reset_type with RECOVERY_IN_PROGRESS

elapsed time: 2063m

configs tested: 131
configs skipped: 1

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

tested configs:
alpha                            allnoconfig    gcc-16.1.0
alpha                           allyesconfig    gcc-16.1.0
alpha                              defconfig    gcc-16.1.0
arc                             allmodconfig    gcc-16.1.0
arc                             allyesconfig    gcc-16.1.0
arc                  randconfig-001-20260717    gcc-11.5.0
arc                  randconfig-002-20260717    gcc-13.4.0
arm                  randconfig-001-20260717    clang-24
arm                  randconfig-002-20260717    clang-24
arm                  randconfig-003-20260717    gcc-8.5.0
arm                  randconfig-004-20260717    gcc-11.5.0
arm                          u8500_defconfig    gcc-16.1.0
arm64                         randconfig-001    gcc-14.3.0
arm64                randconfig-001-20260717    gcc-11.5.0
arm64                randconfig-002-20260717    gcc-8.5.0
arm64                randconfig-003-20260717    gcc-8.5.0
arm64                randconfig-004-20260717    gcc-9.5.0
csky                            allmodconfig    gcc-16.1.0
csky                 randconfig-001-20260717    gcc-13.4.0
csky                 randconfig-002-20260717    gcc-15.2.0
hexagon              randconfig-001-20260717    clang-24
hexagon              randconfig-002-20260717    clang-24
i386                            allmodconfig    gcc-14
i386       buildonly-randconfig-001-20260717    gcc-14
i386       buildonly-randconfig-002-20260717    gcc-14
i386       buildonly-randconfig-003-20260717    clang-22
i386       buildonly-randconfig-004-20260717    gcc-14
i386       buildonly-randconfig-005-20260717    gcc-14
i386       buildonly-randconfig-006-20260717    clang-22
i386                 randconfig-001-20260717    gcc-14
i386                 randconfig-001-20260718    gcc-14
i386                 randconfig-002-20260717    clang-22
i386                 randconfig-002-20260718    gcc-14
i386                 randconfig-003-20260717    clang-22
i386                 randconfig-003-20260718    gcc-14
i386                 randconfig-004-20260717    gcc-14
i386                 randconfig-004-20260718    gcc-14
i386                 randconfig-005-20260717    gcc-14
i386                 randconfig-005-20260718    gcc-14
i386                 randconfig-006-20260717    gcc-14
i386                 randconfig-006-20260718    gcc-14
i386                 randconfig-007-20260717    gcc-14
i386                 randconfig-007-20260718    gcc-14
i386                 randconfig-011-20260717    clang-22
i386                 randconfig-011-20260718    clang-22
i386                 randconfig-012-20260717    clang-22
i386                 randconfig-012-20260718    clang-22
i386                 randconfig-013-20260717    clang-22
i386                 randconfig-013-20260718    clang-22
i386                 randconfig-014-20260717    clang-22
i386                 randconfig-014-20260718    clang-22
i386                 randconfig-015-20260717    clang-22
i386                 randconfig-015-20260718    clang-22
i386                 randconfig-016-20260717    gcc-14
i386                 randconfig-016-20260718    clang-22
i386                 randconfig-017-20260717    clang-22
i386                 randconfig-017-20260718    clang-22
loongarch                          defconfig    clang-24
loongarch            randconfig-001-20260717    gcc-16.1.0
loongarch            randconfig-002-20260717    clang-24
m68k                       mvme147_defconfig    gcc-16.1.0
nios2                           allmodconfig    gcc-11.5.0
nios2                            allnoconfig    gcc-11.5.0
nios2                randconfig-001-20260717    gcc-8.5.0
nios2                randconfig-002-20260717    gcc-8.5.0
parisc                          allmodconfig    gcc-16.1.0
parisc                          allyesconfig    gcc-16.1.0
parisc                             defconfig    gcc-16.1.0
parisc                        randconfig-001    gcc-8.5.0
parisc               randconfig-001-20260717    gcc-10.5.0
parisc               randconfig-002-20260717    gcc-16.1.0
powerpc              randconfig-001-20260717    gcc-13.4.0
powerpc              randconfig-002-20260717    gcc-11.5.0
powerpc64            randconfig-001-20260717    gcc-15.2.0
powerpc64            randconfig-002-20260717    gcc-8.5.0
riscv                randconfig-001-20260717    clang-16
riscv                randconfig-001-20260718    clang-24
riscv                randconfig-002-20260717    clang-24
riscv                randconfig-002-20260718    clang-24
s390                 randconfig-001-20260717    clang-24
s390                 randconfig-001-20260718    clang-24
s390                 randconfig-002-20260717    clang-20
s390                 randconfig-002-20260718    clang-24
sh                                 defconfig    gcc-16.1.0
sh                   randconfig-001-20260717    gcc-9.5.0
sh                   randconfig-001-20260718    clang-24
sh                   randconfig-002-20260717    gcc-16.1.0
sh                   randconfig-002-20260718    clang-24
sparc                randconfig-001-20260717    gcc-8.5.0
sparc                randconfig-001-20260718    gcc-11.5.0
sparc                randconfig-002-20260717    gcc-14.3.0
sparc                randconfig-002-20260718    gcc-11.5.0
sparc64              randconfig-001-20260717    clang-22
sparc64              randconfig-001-20260718    gcc-11.5.0
sparc64              randconfig-002-20260717    clang-24
sparc64              randconfig-002-20260718    gcc-11.5.0
um                   randconfig-001-20260717    clang-24
um                   randconfig-001-20260718    gcc-11.5.0
um                   randconfig-002-20260717    clang-21
um                   randconfig-002-20260718    gcc-11.5.0
x86_64              buildonly-randconfig-001    gcc-12
x86_64     buildonly-randconfig-001-20260717    gcc-14
x86_64     buildonly-randconfig-002-20260717    gcc-14
x86_64     buildonly-randconfig-003-20260717    gcc-14
x86_64     buildonly-randconfig-004-20260717    gcc-14
x86_64     buildonly-randconfig-005-20260717    clang-22
x86_64     buildonly-randconfig-006-20260717    clang-22
x86_64                                 kexec    clang-22
x86_64               randconfig-001-20260717    clang-22
x86_64               randconfig-002-20260717    clang-22
x86_64               randconfig-003-20260717    gcc-14
x86_64               randconfig-004-20260717    gcc-14
x86_64               randconfig-005-20260717    gcc-14
x86_64               randconfig-006-20260717    gcc-13
x86_64               randconfig-011-20260717    gcc-14
x86_64               randconfig-012-20260717    gcc-14
x86_64               randconfig-013-20260717    clang-22
x86_64               randconfig-014-20260717    gcc-12
x86_64               randconfig-015-20260717    gcc-14
x86_64               randconfig-016-20260717    gcc-14
x86_64               randconfig-071-20260717    gcc-14
x86_64               randconfig-072-20260717    gcc-14
x86_64               randconfig-073-20260717    gcc-14
x86_64               randconfig-074-20260717    gcc-12
x86_64               randconfig-075-20260717    gcc-14
x86_64               randconfig-076-20260717    clang-22
x86_64                          rhel-9.4-bpf    gcc-14
xtensa               randconfig-001-20260717    gcc-16.1.0
xtensa               randconfig-001-20260718    gcc-11.5.0
xtensa               randconfig-002-20260717    gcc-14.3.0
xtensa               randconfig-002-20260718    gcc-11.5.0

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

^ permalink raw reply

* RE: Bluetooth: Replace HCI_VENDOR_PKT usages with HCI_EV_VENDOR
From: bluez.test.bot @ 2026-07-17 23:27 UTC (permalink / raw)
  To: linux-bluetooth, zijun.hu
In-Reply-To: <20260717-fix_vendor-v1-1-f22263c1175b@oss.qualcomm.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      1.75 seconds
VerifyFixes                   PASS      0.15 seconds
VerifySignedoff               PASS      0.20 seconds
GitLint                       PASS      0.98 seconds
SubjectPrefix                 PASS      0.30 seconds
BuildKernel                   PASS      24.91 seconds
CheckAllWarning               PASS      27.22 seconds
CheckSparse                   PASS      26.01 seconds
BuildKernel32                 PASS      23.86 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      453.70 seconds
IncrementalBuild              PASS      28.93 seconds

Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* RE: Bluetooth: btbcm: Add entry for BCM4356A3 UART bluetooth
From: bluez.test.bot @ 2026-07-17 22:35 UTC (permalink / raw)
  To: linux-bluetooth, webgeek1234
In-Reply-To: <20260717-bcm4356a3-bt-v1-1-ffa2d5233360@gmail.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      0.59 seconds
VerifyFixes                   PASS      0.09 seconds
VerifySignedoff               PASS      0.08 seconds
GitLint                       PASS      1.51 seconds
SubjectPrefix                 PASS      0.67 seconds
BuildKernel                   PASS      29.41 seconds
CheckAllWarning               PASS      32.67 seconds
CheckSparse                   PASS      30.24 seconds
BuildKernel32                 PASS      28.53 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      528.47 seconds
IncrementalBuild              PASS      30.05 seconds

Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [PATCH 3/3] Bluetooth: btusb: Realtek: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR
From: Zijun Hu @ 2026-07-17 22:08 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
  Cc: Zijun Hu, linux-arm-msm, linux-bluetooth, linux-kernel, Zijun Hu
In-Reply-To: <20260717-fix_vendor-v1-0-f22263c1175b@oss.qualcomm.com>

The macros below have different meanings even though they share the
same value 0xff:

  HCI_VENDOR_PKT: HCI packet indicator or type
  HCI_EV_VENDOR:  event code of a VSE

This usage of HCI_VENDOR_PKT is wrongly checking an event code.

Fix by using HCI_EV_VENDOR for event code.

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
 drivers/bluetooth/btusb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index d69eec8b2911..6973daab91ec 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2799,7 +2799,7 @@ static int btusb_setup_realtek(struct hci_dev *hdev)
 
 static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
+	if (skb->data[0] == HCI_EV_VENDOR && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
 		struct rtk_dev_coredump_hdr hdr = {
 			.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
 		};

-- 
2.34.1


^ permalink raw reply related

* [PATCH 2/3] Bluetooth: btusb: QCA: Replace HCI_VENDOR_PKT usages with HCI_EV_VENDOR
From: Zijun Hu @ 2026-07-17 22:08 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
  Cc: Zijun Hu, linux-arm-msm, linux-bluetooth, linux-kernel, Zijun Hu
In-Reply-To: <20260717-fix_vendor-v1-0-f22263c1175b@oss.qualcomm.com>

The macros below have different meanings even though they share the
same value 0xff:

  HCI_VENDOR_PKT: HCI packet indicator or type
  HCI_EV_VENDOR:  event code of a VSE

These usages of HCI_VENDOR_PKT are wrongly checking an event code.

Fix by using HCI_EV_VENDOR for event code.
Also fix warning "CHECK: Unnecessary parentheses around comparison"
given by checkpatch.pl.

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
 drivers/bluetooth/btusb.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 4c3193ec2d52..d69eec8b2911 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -3276,7 +3276,7 @@ static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
 		goto out;
 
 	event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
-	if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
+	if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
 		goto out;
 
 	dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
@@ -3302,7 +3302,7 @@ static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
 		return false;
 
 	event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
-	if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
+	if (!event_hdr || event_hdr->evt != HCI_EV_VENDOR)
 		goto out;
 
 	dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));

-- 
2.34.1


^ permalink raw reply related

* [PATCH 0/3] Bluetooth: Replace HCI_VENDOR_PKT usages with HCI_EV_VENDOR
From: Zijun Hu @ 2026-07-17 22:08 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
  Cc: Zijun Hu, linux-arm-msm, linux-bluetooth, linux-kernel, Zijun Hu

The macros below have different meanings even though they share the
same value 0xff:

 HCI_VENDOR_PKT: HCI packet indicator or type
 HCI_EV_VENDOR:  event code of a VSE

several usage of HCI_VENDOR_PKT is wrongly checking an event code.

Fix by using HCI_EV_VENDOR for event code.

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
Zijun Hu (3):
      Bluetooth: hci_qca: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR
      Bluetooth: btusb: QCA: Replace HCI_VENDOR_PKT usages with HCI_EV_VENDOR
      Bluetooth: btusb: Realtek: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR

 drivers/bluetooth/btusb.c   | 6 +++---
 drivers/bluetooth/hci_qca.c | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)
---
base-commit: bd8bee79e1fa8db2d587733f8b6fd9597b04d6e3
change-id: 20260717-fix_vendor-6f40af4b1c91

Best regards,
-- 
Zijun Hu <zijun.hu@oss.qualcomm.com>


^ permalink raw reply

* [PATCH 1/3] Bluetooth: hci_qca: Replace HCI_VENDOR_PKT usage with HCI_EV_VENDOR
From: Zijun Hu @ 2026-07-17 22:08 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
  Cc: Zijun Hu, linux-arm-msm, linux-bluetooth, linux-kernel, Zijun Hu
In-Reply-To: <20260717-fix_vendor-v1-0-f22263c1175b@oss.qualcomm.com>

The macros below have different meanings even though they share the
same value 0xff:

  HCI_VENDOR_PKT: HCI packet indicator or type
  HCI_EV_VENDOR:  event code of a VSE

This usage of HCI_VENDOR_PKT is wrongly checking an event code.

Fix by using HCI_EV_VENDOR for event code.
Also fix warning "CHECK: Unnecessary parentheses around comparison"
given by checkpatch.pl.

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
 drivers/bluetooth/hci_qca.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index bd29d422c209..f978087612ee 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1240,8 +1240,8 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
 	 * received we store dump into a file before closing hci. This
 	 * dump will help in triaging the issues.
 	 */
-	if ((skb->data[0] == HCI_VENDOR_PKT) &&
-	    (get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE))
+	if (skb->data[0] == HCI_EV_VENDOR &&
+	    get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE)
 		return qca_controller_memdump_event(hdev, skb);
 
 	return hci_recv_frame(hdev, skb);

-- 
2.34.1


^ permalink raw reply related

* [PATCH] Bluetooth: btbcm: Add entry for BCM4356A3 UART bluetooth
From: Aaron Kling via B4 Relay @ 2026-07-17 21:40 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-kernel, Aaron Kling

From: Aaron Kling <webgeek1234@gmail.com>

This patch adds the device ID for the bluetooth chip used in the
Nvidia specific Broadcom BCM4356 PCI-E WiFi / UART BT chip.

Signed-off-by: Aaron Kling <webgeek1234@gmail.com>
---
 drivers/bluetooth/btbcm.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 463d59890befe..14acea7325478 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -506,6 +506,7 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
 	{ 0x6606, "BCM4345C5"	},	/* 003.006.006 */
 	{ 0x230f, "BCM4356A2"	},	/* 001.003.015 */
 	{ 0x2310, "BCM4343A2"	},	/* 001.003.016 */
+	{ 0x2409, "BCM4356A3"	},	/* 001.004.009 */
 	{ 0x220e, "BCM20702A1"  },	/* 001.002.014 */
 	{ 0x420d, "BCM4349B1"	},	/* 002.002.013 */
 	{ 0x420e, "BCM4349B1"	},	/* 002.002.014 */

---
base-commit: 49362394dad7df66c274c867a271394c10ca2bb8
change-id: 20260717-bcm4356a3-bt-cd78cc0f42e8

Best regards,
-- 
Aaron Kling <webgeek1234@gmail.com>



^ permalink raw reply related

* RE: Bluetooth: HIDP: add missing length check for incoming frames
From: bluez.test.bot @ 2026-07-17 16:23 UTC (permalink / raw)
  To: linux-bluetooth, yaojiale02
In-Reply-To: <20260717153238.2002330-1-yaojiale02@163.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      0.58 seconds
VerifyFixes                   PASS      0.10 seconds
VerifySignedoff               PASS      0.11 seconds
GitLint                       PASS      0.27 seconds
SubjectPrefix                 PASS      0.16 seconds
BuildKernel                   PASS      22.57 seconds
CheckAllWarning               PASS      25.42 seconds
CheckSparse                   PASS      25.04 seconds
BuildKernel32                 PASS      22.34 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      408.54 seconds
TestRunner_l2cap-tester       PASS      56.29 seconds
TestRunner_iso-tester         PASS      78.41 seconds
TestRunner_bnep-tester        PASS      17.10 seconds
TestRunner_mgmt-tester        FAIL      204.30 seconds
TestRunner_rfcomm-tester      PASS      23.90 seconds
TestRunner_sco-tester         PASS      30.72 seconds
TestRunner_ioctl-tester       PASS      23.78 seconds
TestRunner_mesh-tester        FAIL      23.76 seconds
TestRunner_smp-tester         PASS      21.47 seconds
TestRunner_userchan-tester    PASS      18.22 seconds
TestRunner_6lowpan-tester     PASS      20.95 seconds
IncrementalBuild              PASS      21.80 seconds

Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4

Failed Test Cases
Read Exp Feature - Success                           Failed       0.232 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0

Failed Test Cases
Mesh - Send cancel - 1                               Timed out    1.977 seconds
Mesh - Send cancel - 2                               Timed out    1.988 seconds


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH] Bluetooth: MGMT: dequeue queued mesh send before freeing canceled tx
From: Cen Zhang @ 2026-07-17 15:55 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Marcel Holtmann, linux-bluetooth, baijiaju1990
In-Reply-To: <CABBYNZKQKpt9gdJUYb6YS_6ryGZLjrofO0OEpu4jsvmA7f00uw@mail.gmail.com>

Hi Luiz,

Luiz Augusto von Dentz <luiz.dentz@gmail.com> 于2026年7月17日周五 23:50写道:
>
> If you really want to bump it just resend it. Sending pings like this
> won't retrigger the CI to pick it up and test it.

Thanks for the clarification. I'll resend it next week.

Best regards,
Cen Zhang

^ permalink raw reply

* Re: [PATCH] Bluetooth: MGMT: dequeue queued mesh send before freeing canceled tx
From: Luiz Augusto von Dentz @ 2026-07-17 15:50 UTC (permalink / raw)
  To: Cen Zhang; +Cc: Marcel Holtmann, linux-bluetooth, baijiaju1990
In-Reply-To: <CAFRLqsVdaKE63gAKVuAjv++CM1z8hX1hYh6N3BxHkVaoyKnKVw@mail.gmail.com>

Hi Cen,

On Fri, Jul 17, 2026 at 11:46 AM Cen Zhang <zzzccc427@gmail.com> wrote:
>
> Hi Marcel, Luiz,
>
> Gentle ping on this patch.
>
> Could you please take a look when you have a chance?

If you really want to bump it just resend it. Sending pings like this
won't retrigger the CI to pick it up and test it.

> Thanks!
>
> Best regards,
> Cen Zhang
>
> Cen Zhang <zzzccc427@gmail.com> 于2026年7月3日周五 14:00写道:
> >
> > mesh_next() queues mesh_send_sync() with a raw mgmt_mesh_tx pointer after
> > a previous mesh send completes. MGMT_OP_MESH_SEND_CANCEL can already be
> > queued on the same hci_cmd_sync_work list. If cancel runs before the queued
> > send, it can remove and free the same mgmt_mesh_tx while mesh_send_sync()
> > still carries that pointer as callback data.
> >
> > The buggy scenario involves two paths, with each column showing the order
> > within that path:
> >
> >   mesh completion path:              cancel command path:
> >   1. mesh_send_done_sync() returns   1. send_cancel is already queued
> >   2. mesh_next() selects next_tx     2. send_cancel finds next_tx
> >   3. mesh_next() queues              3. send_cancel frees next_tx
> >      mesh_send_sync(next_tx)
> >   4. mesh_send_sync() later
> >      dereferences next_tx
> >
> > Validation reproduced this kernel report:
> > BUG: KASAN: slab-use-after-free in mesh_send_sync+0x5a/0x1c0 [bluetooth]
> > Read of size 1 at addr ffff88811a01d13b by task kworker/u17:0/562
> > Workqueue: hci0 hci_cmd_sync_work [bluetooth]
> >
> > Call Trace:
> >  <TASK>
> >  dump_stack_lvl+0x66/0xa0
> >  print_report+0xce/0x5f0
> >  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
> >  ? __virt_addr_valid+0x19f/0x330
> >  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
> >  kasan_report+0xe0/0x110
> >  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
> >  ? __pfx_mesh_send_sync+0x10/0x10 [bluetooth]
> >  mesh_send_sync+0x5a/0x1c0 [bluetooth]
> >  ? mesh_send_sync+0x9/0x1c0 [bluetooth]
> >  ? __pfx_mesh_send_sync+0x10/0x10 [bluetooth]
> >  hci_cmd_sync_work+0x187/0x210 [bluetooth]
> >  process_one_work+0x4fd/0xbc0
> >  worker_thread+0x2d8/0x570
> >  kthread+0x1ad/0x1f0
> >  ret_from_fork+0x3c9/0x540
> >  ret_from_fork_asm+0x1a/0x30
> >
> > Allocated by task 602:
> >  kasan_save_stack+0x33/0x60
> >  kasan_save_track+0x17/0x60
> >  __kasan_kmalloc+0xaa/0xb0
> >  mgmt_mesh_add+0x41/0x1a0 [bluetooth]
> >  mesh_send+0x197/0x3a0 [bluetooth]
> >  hci_sock_sendmsg+0x96b/0xf80 [bluetooth]
> >  __sys_sendto+0x2bc/0x2d0
> >  __x64_sys_sendto+0x76/0x90
> >  do_syscall_64+0x115/0x6a0
> >  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> >
> > Freed by task 562:
> >  kasan_save_stack+0x33/0x60
> >  kasan_save_track+0x17/0x60
> >  kasan_save_free_info+0x3b/0x60
> >  __kasan_slab_free+0x5f/0x80
> >  kfree+0x313/0x590
> >  send_cancel+0x1d8/0x210 [bluetooth]
> >  hci_cmd_sync_work+0x187/0x210 [bluetooth]
> >  process_one_work+0x4fd/0xbc0
> >  worker_thread+0x2d8/0x570
> >  kthread+0x1ad/0x1f0
> >  ret_from_fork+0x3c9/0x540
> >  ret_from_fork_asm+0x1a/0x30
> >
> > Dequeue any queued mesh_send_sync() for the target tx from send_cancel().
> > When a queued send is found, the dequeue path invokes
> > mesh_send_start_complete(), which completes and frees the tx; send_cancel()
> > must not complete it again.
> >
> > Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh")
> > Assisted-by: Codex:gpt-5.5
> > Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> > ---
> >  net/bluetooth/mgmt.c | 23 +++++++++++++++++------
> >  1 file changed, 17 insertions(+), 6 deletions(-)
> >
> > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> > index 733a4b70e10c..ac4922cb39b5 100644
> > --- a/net/bluetooth/mgmt.c
> > +++ b/net/bluetooth/mgmt.c
> > @@ -2416,17 +2416,28 @@ static int send_cancel(struct hci_dev *hdev, void *data)
> >         struct mgmt_mesh_tx *mesh_tx;
> >
> >         if (!cancel->handle) {
> > -               do {
> > +               for (;;) {
> >                         mesh_tx = mgmt_mesh_next(hdev, cmd->sk);
> >
> > -                       if (mesh_tx)
> > -                               mesh_send_complete(hdev, mesh_tx, false);
> > -               } while (mesh_tx);
> > +                       if (!mesh_tx)
> > +                               break;
> > +
> > +                       /* Dequeue any queued send before freeing the tx. */
> > +                       if (hci_cmd_sync_dequeue(hdev, mesh_send_sync, mesh_tx,
> > +                                                mesh_send_start_complete))
> > +                               continue;
> > +
> > +                       mesh_send_complete(hdev, mesh_tx, false);
> > +               }
> >         } else {
> >                 mesh_tx = mgmt_mesh_find(hdev, cancel->handle);
> >
> > -               if (mesh_tx && mesh_tx->sk == cmd->sk)
> > -                       mesh_send_complete(hdev, mesh_tx, false);
> > +               if (mesh_tx && mesh_tx->sk == cmd->sk) {
> > +                       /* Dequeue any queued send before freeing the tx. */
> > +                       if (!hci_cmd_sync_dequeue(hdev, mesh_send_sync, mesh_tx,
> > +                                                 mesh_send_start_complete))
> > +                               mesh_send_complete(hdev, mesh_tx, false);
> > +               }
> >         }
> >
> >         mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
> > --
> > 2.43.0



-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH] Bluetooth: MGMT: Fix ADD_EXT_ADV_DATA rollback of committed instances
From: Cen Zhang @ 2026-07-17 15:47 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, baijiaju1990
In-Reply-To: <20260703060033.570932-1-zzzccc427@gmail.com>

Hi Marcel, Luiz,

Gentle ping on this patch.

Could you please take a look when you have a chance?

Thanks!

Best regards,
Cen Zhang

Cen Zhang <zzzccc427@gmail.com> 于2026年7月3日周五 14:00写道:
>
> add_ext_adv_data() uses clear_new_instance to roll back failures after
> preparing advertising data. That path is valid for a newly created pending
> instance, but a malformed ADD_EXT_ADV_DATA request can also reach it for
> an already committed advertising instance. In that case clear_new_instance
> frees the live adv_info while hci_cmd_sync_work() may still be using it.
>
> The buggy scenario involves two paths, with each column showing the order
> within that path:
>
>   advertising enable path:           ADD_EXT_ADV_DATA error path:
>   1. look up the committed adv_info  1. accept an update for that instance
>   2. wait for the controller         2. hit clear_new_instance on failure
>      response                        3. remove and free the live adv_info
>   3. resume and access adv_info
>
> Validation reproduced this kernel report:
> BUG: KASAN: slab-use-after-free in hci_enable_ext_advertising_sync+0x99/0x190 [bluetooth]
> Read of size 1 at addr ffff888104e3f010 by task kworker/u17:1/2502
> Workqueue: hci0 hci_cmd_sync_work [bluetooth]
>
> Call Trace:
>  <TASK>
>  dump_stack_lvl+0x66/0xa0
>  print_report+0xce/0x5f0
>  ? hci_enable_ext_advertising_sync+0x99/0x190 [bluetooth]
>  ? __virt_addr_valid+0x19f/0x330
>  ? hci_enable_ext_advertising_sync+0x99/0x190 [bluetooth]
>  kasan_report+0xe0/0x110
>  ? hci_enable_ext_advertising_sync+0x99/0x190 [bluetooth]
>  ? __pfx_add_ext_adv_data_sync+0x10/0x10 [bluetooth]
>  hci_enable_ext_advertising_sync+0x99/0x190 [bluetooth]
>  ? __pfx_hci_enable_ext_advertising_sync+0x10/0x10 [bluetooth]
>  hci_cmd_sync_work+0x187/0x210 [bluetooth]
>  process_one_work+0x4fd/0xbc0
>  worker_thread+0x2d8/0x570
>  kthread+0x1ad/0x1f0
>  ret_from_fork+0x3c9/0x540
>  ret_from_fork_asm+0x1a/0x30
>
> Allocated by task 2515:
>  kasan_save_stack+0x33/0x60
>  kasan_save_track+0x17/0x60
>  __kasan_kmalloc+0xaa/0xb0
>  hci_add_adv_instance+0x35d/0x440 [bluetooth]
>  add_ext_adv_params+0x291/0x510 [bluetooth]
>  hci_sock_sendmsg+0x96b/0xf80 [bluetooth]
>  __sys_sendto+0x2bc/0x2d0
>  __x64_sys_sendto+0x76/0x90
>  do_syscall_64+0x115/0x6a0
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Freed by task 2515:
>  kasan_save_stack+0x33/0x60
>  kasan_save_track+0x17/0x60
>  kasan_save_free_info+0x3b/0x60
>  __kasan_slab_free+0x5f/0x80
>  kfree+0x313/0x590
>  hci_remove_adv_instance+0x130/0x1b0 [bluetooth]
>  add_ext_adv_data+0x263/0x640 [bluetooth]
>  hci_sock_sendmsg+0x96b/0xf80 [bluetooth]
>  __sys_sendto+0x2bc/0x2d0
>  __x64_sys_sendto+0x76/0x90
>  do_syscall_64+0x115/0x6a0
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Only pending instances should be rolled back from ADD_EXT_ADV_DATA
> failures. Leave committed instances in place so queued controller work
> cannot retain a freed adv_info pointer.
>
> Fixes: 12410572833a2 ("Bluetooth: Break add adv into two mgmt commands")
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> ---
>  net/bluetooth/mgmt.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 733a4b70e10c..a4126d616efa 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -9220,7 +9220,8 @@ static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data,
>         goto unlock;
>
>  clear_new_instance:
> -       hci_remove_adv_instance(hdev, cp->instance);
> +       if (adv_instance->pending)
> +               hci_remove_adv_instance(hdev, cp->instance);
>
>  unlock:
>         hci_dev_unlock(hdev);
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH v2] Bluetooth: hci_debugfs: serialize force_bredr_smp writes
From: Cen Zhang @ 2026-07-17 15:46 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, baijiaju1990
In-Reply-To: <20260703081431.922054-1-zzzccc427@gmail.com>

Hi Marcel, Luiz,

Gentle ping on this patch.

Could you please take a look when you have a chance?

Thanks!

Best regards,
Cen Zhang

Cen Zhang <zzzccc427@gmail.com> 于2026年7月3日周五 16:14写道:
>
> force_bredr_smp_write() calls smp_force_bredr() without hci_dev_lock().
> That helper checks HCI_FORCE_BREDR_SMP, updates hdev->smp_bredr_data,
> and then toggles the force flag.
>
> Two same-value writers can both pass the state check and run the same
> enable or disable transition on one hdev. On the disable side, one writer
> can clear hdev->smp_bredr_data while the other still observes the old
> force flag and later passes NULL into smp_del_chan(). The double
> transition can also leave the force flag out of sync with the requested
> state.
>
> Take hci_dev_lock() around smp_force_bredr() in the debugfs write path so
> each request observes and applies one stable BR/EDR SMP transition.
>
> Validation reproduced this kernel report:
>
>   KASAN null-ptr-deref in smp_del_chan+0x31/0x90
>   RIP: 0033:0x7faae680d340
>   RIP: 0010:smp_del_chan+0x31/0x90 [bluetooth]
>   Read of size 8
>   Call Trace:
>    dump_stack_lvl+0x66/0xa0
>    kasan_report+0xe0/0x110
>    smp_del_chan+0x31/0x90
>    smp_force_bredr+0x69/0xc0
>    trace_clock_x86_tsc+0x20/0x20
>    srso_alias_return_thunk+0x5/0xfbef5
>    lock_acquire+0xd0/0x300
>    ksys_write+0xd2/0x170
>    full_proxy_write+0x9e/0xd0
>    vfs_write+0x1b0/0x810
>    find_held_lock+0x2b/0x80
>    do_user_addr_fault+0x65a/0x890
>    rcu_is_watching+0x20/0x50
>    do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87)
>    entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Fixes: 300acfdec916 ("Bluetooth: Introduce force_bredr_smp debugfs option for testing")
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> ---
> v2:
> - Reword commit message to avoid checkpatch's 75-character line warning.
>
>  net/bluetooth/hci_debugfs.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
> index b7f682922a16..ff344564c923 100644
> --- a/net/bluetooth/hci_debugfs.c
> +++ b/net/bluetooth/hci_debugfs.c
> @@ -520,7 +520,9 @@ static ssize_t force_bredr_smp_write(struct file *file,
>         if (err)
>                 return err;
>
> +       hci_dev_lock(hdev);
>         err = smp_force_bredr(hdev, enable);
> +       hci_dev_unlock(hdev);
>         if (err)
>                 return err;
>
> --
> 2.43.0

^ permalink raw reply

* Re: [PATCH] Bluetooth: MGMT: dequeue queued mesh send before freeing canceled tx
From: Cen Zhang @ 2026-07-17 15:46 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz; +Cc: linux-bluetooth, baijiaju1990
In-Reply-To: <20260703060024.570535-1-zzzccc427@gmail.com>

Hi Marcel, Luiz,

Gentle ping on this patch.

Could you please take a look when you have a chance?

Thanks!

Best regards,
Cen Zhang

Cen Zhang <zzzccc427@gmail.com> 于2026年7月3日周五 14:00写道:
>
> mesh_next() queues mesh_send_sync() with a raw mgmt_mesh_tx pointer after
> a previous mesh send completes. MGMT_OP_MESH_SEND_CANCEL can already be
> queued on the same hci_cmd_sync_work list. If cancel runs before the queued
> send, it can remove and free the same mgmt_mesh_tx while mesh_send_sync()
> still carries that pointer as callback data.
>
> The buggy scenario involves two paths, with each column showing the order
> within that path:
>
>   mesh completion path:              cancel command path:
>   1. mesh_send_done_sync() returns   1. send_cancel is already queued
>   2. mesh_next() selects next_tx     2. send_cancel finds next_tx
>   3. mesh_next() queues              3. send_cancel frees next_tx
>      mesh_send_sync(next_tx)
>   4. mesh_send_sync() later
>      dereferences next_tx
>
> Validation reproduced this kernel report:
> BUG: KASAN: slab-use-after-free in mesh_send_sync+0x5a/0x1c0 [bluetooth]
> Read of size 1 at addr ffff88811a01d13b by task kworker/u17:0/562
> Workqueue: hci0 hci_cmd_sync_work [bluetooth]
>
> Call Trace:
>  <TASK>
>  dump_stack_lvl+0x66/0xa0
>  print_report+0xce/0x5f0
>  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
>  ? __virt_addr_valid+0x19f/0x330
>  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
>  kasan_report+0xe0/0x110
>  ? mesh_send_sync+0x5a/0x1c0 [bluetooth]
>  ? __pfx_mesh_send_sync+0x10/0x10 [bluetooth]
>  mesh_send_sync+0x5a/0x1c0 [bluetooth]
>  ? mesh_send_sync+0x9/0x1c0 [bluetooth]
>  ? __pfx_mesh_send_sync+0x10/0x10 [bluetooth]
>  hci_cmd_sync_work+0x187/0x210 [bluetooth]
>  process_one_work+0x4fd/0xbc0
>  worker_thread+0x2d8/0x570
>  kthread+0x1ad/0x1f0
>  ret_from_fork+0x3c9/0x540
>  ret_from_fork_asm+0x1a/0x30
>
> Allocated by task 602:
>  kasan_save_stack+0x33/0x60
>  kasan_save_track+0x17/0x60
>  __kasan_kmalloc+0xaa/0xb0
>  mgmt_mesh_add+0x41/0x1a0 [bluetooth]
>  mesh_send+0x197/0x3a0 [bluetooth]
>  hci_sock_sendmsg+0x96b/0xf80 [bluetooth]
>  __sys_sendto+0x2bc/0x2d0
>  __x64_sys_sendto+0x76/0x90
>  do_syscall_64+0x115/0x6a0
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Freed by task 562:
>  kasan_save_stack+0x33/0x60
>  kasan_save_track+0x17/0x60
>  kasan_save_free_info+0x3b/0x60
>  __kasan_slab_free+0x5f/0x80
>  kfree+0x313/0x590
>  send_cancel+0x1d8/0x210 [bluetooth]
>  hci_cmd_sync_work+0x187/0x210 [bluetooth]
>  process_one_work+0x4fd/0xbc0
>  worker_thread+0x2d8/0x570
>  kthread+0x1ad/0x1f0
>  ret_from_fork+0x3c9/0x540
>  ret_from_fork_asm+0x1a/0x30
>
> Dequeue any queued mesh_send_sync() for the target tx from send_cancel().
> When a queued send is found, the dequeue path invokes
> mesh_send_start_complete(), which completes and frees the tx; send_cancel()
> must not complete it again.
>
> Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh")
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> ---
>  net/bluetooth/mgmt.c | 23 +++++++++++++++++------
>  1 file changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 733a4b70e10c..ac4922cb39b5 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -2416,17 +2416,28 @@ static int send_cancel(struct hci_dev *hdev, void *data)
>         struct mgmt_mesh_tx *mesh_tx;
>
>         if (!cancel->handle) {
> -               do {
> +               for (;;) {
>                         mesh_tx = mgmt_mesh_next(hdev, cmd->sk);
>
> -                       if (mesh_tx)
> -                               mesh_send_complete(hdev, mesh_tx, false);
> -               } while (mesh_tx);
> +                       if (!mesh_tx)
> +                               break;
> +
> +                       /* Dequeue any queued send before freeing the tx. */
> +                       if (hci_cmd_sync_dequeue(hdev, mesh_send_sync, mesh_tx,
> +                                                mesh_send_start_complete))
> +                               continue;
> +
> +                       mesh_send_complete(hdev, mesh_tx, false);
> +               }
>         } else {
>                 mesh_tx = mgmt_mesh_find(hdev, cancel->handle);
>
> -               if (mesh_tx && mesh_tx->sk == cmd->sk)
> -                       mesh_send_complete(hdev, mesh_tx, false);
> +               if (mesh_tx && mesh_tx->sk == cmd->sk) {
> +                       /* Dequeue any queued send before freeing the tx. */
> +                       if (!hci_cmd_sync_dequeue(hdev, mesh_send_sync, mesh_tx,
> +                                                 mesh_send_start_complete))
> +                               mesh_send_complete(hdev, mesh_tx, false);
> +               }
>         }
>
>         mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
> --
> 2.43.0

^ permalink raw reply

* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding
From: Luiz Augusto von Dentz @ 2026-07-17 15:44 UTC (permalink / raw)
  To: Prathibha Madugonde
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <CABBYNZL1rf4LbLNiese8DwRBMMgtCps-VH-nNCbZ9G1Xu=1RBQ@mail.gmail.com>

Hi,

On Fri, Jul 17, 2026 at 11:17 AM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi.
>
> On Fri, Jul 17, 2026 at 11:07 AM Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
> >
> > Hi Prathibha,
> >
> > On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde
> > <prathibha.madugonde@oss.qualcomm.com> wrote:
> > >
> > > From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
> > >
> > > Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results,
> > > and CS/procedure-enable config) into an a{sv} dictionary and emit it
> > > as a new ProcedureData signal on the CS D-Bus interface.
> >
> > Was this documented though? It doesn't seem to follow the likes of
> > https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst,
> > for instance we don't use camel case for dictionary options. Also, if
> > this is a result of `StartMeasurement`, we should probably signal it
> > as Results or something like that, but I'd start with documentation so
> > we can agree on the overall design.
>
> Ok, looks like this does follow the documentation already:
>
> https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst#void-proceduredatadict-data
>
> That said that will probably need updating since some dictionaries use
> camel case and others don't, so this needs to converge to a single
> format.

I chatted with Marcel, and we concluded this should probably go as a
data blob since the fields are somewhat analog and meaningless
individually, packing it into a dictionary just to unpack it later is
useless.

> > > Register a procedure_data callback on the RAP HCI state machine so
> > > rap.c is notified when a procedure completes, and propagate the
> > > locally supported T_SW time capability from the read-local-supported-
> > > capabilities response into bt_rap.
> > >
> > > ---
> > >  profiles/ranging/rap.c     | 497 ++++++++++++++++++++++++++++++++++++-
> > >  profiles/ranging/rap_hci.c |  59 +++++
> > >  2 files changed, 555 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
> > > index 3ffc0da76..42e1ef2a4 100644
> > > --- a/profiles/ranging/rap.c
> > > +++ b/profiles/ranging/rap.c
> > > @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc(
> > >         return NULL;
> > >  }
> > >
> > > +static void dict_append_byte(DBusMessageIter *dict, const char *key,
> > > +                               uint8_t val)
> > > +{
> > > +       DBusMessageIter entry, variant;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                               &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y",
> > > +                                               &variant);
> > > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void dict_append_int32(DBusMessageIter *dict, const char *key,
> > > +                               dbus_int32_t val)
> > > +{
> > > +       DBusMessageIter entry, variant;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                               &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i",
> > > +                                               &variant);
> > > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void dict_append_uint32(DBusMessageIter *dict, const char *key,
> > > +                               dbus_uint32_t val)
> > > +{
> > > +       DBusMessageIter entry, variant;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                               &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u",
> > > +                                               &variant);
> > > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void dict_append_uint64(DBusMessageIter *dict, const char *key,
> > > +                               dbus_uint64_t val)
> > > +{
> > > +       DBusMessageIter entry, variant;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                               &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t",
> > > +                                               &variant);
> > > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void dict_append_byte_array(DBusMessageIter *dict, const char *key,
> > > +                                       const uint8_t *data, int len)
> > > +{
> > > +       DBusMessageIter entry, variant, arr;
> > > +       int i;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                               &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> > > +                                               &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> > > +       for (i = 0; i < len; i++)
> > > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]);
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void append_mode_zero(DBusMessageIter *dict,
> > > +                               const struct cs_mode_zero_data *m0)
> > > +{
> > > +       dict_append_byte(dict, "packetQuality", m0->packet_quality);
> > > +       dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm);
> > > +       dict_append_byte(dict, "packetAntenna", m0->packet_ant);
> > > +       dict_append_int32(dict, "initiatorMeasuredFreqOffset",
> > > +                               (dbus_int32_t)m0->init_measured_freq_offset);
> > > +}
> > > +
> > > +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key,
> > > +                               const struct pct_iq_sample *pct)
> > > +{
> > > +       DBusMessageIter entry, variant, arr;
> > > +       dbus_int32_t i_s = pct->i_sample;
> > > +       dbus_int32_t q_s = pct->q_sample;
> > > +
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                        &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> > > +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> > > +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void append_mode_one(DBusMessageIter *dict,
> > > +                               const struct cs_mode_one_data *m1)
> > > +{
> > > +       dict_append_byte(dict, "packetQuality", m1->packet_quality);
> > > +       dict_append_byte(dict, "packetNadm", m1->packet_nadm);
> > > +       dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm);
> > > +       dict_append_int32(dict, "toaTodInitiator",
> > > +                               (dbus_int32_t)m1->toa_tod_init);
> > > +       dict_append_int32(dict, "todToaReflector",
> > > +                               (dbus_int32_t)m1->tod_toa_refl);
> > > +       dict_append_byte(dict, "packetAntenna", m1->packet_ant);
> > > +       append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1);
> > > +       append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2);
> > > +}
> > > +
> > > +static void append_mode_two(DBusMessageIter *dict,
> > > +                               const struct cs_mode_two_data *m2,
> > > +                               uint8_t num_ant_paths)
> > > +{
> > > +       DBusMessageIter entry, variant, arr;
> > > +       const char *key;
> > > +       int j;
> > > +       int num_paths;
> > > +
> > > +       /*
> > > +        * num_ant_paths is the HCI "number of antenna paths" value
> > > +        * (0-indexed), so actual tone sample count = num_ant_paths + 1,
> > > +        * capped at array size.
> > > +        */
> > > +       num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ?
> > > +                               (num_ant_paths + 1) : CS_MAX_ANT_PATHS;
> > > +
> > > +       dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index);
> > > +
> > > +       /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */
> > > +       key = "tonePctIQSamples";
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                        &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> > > +       for (j = 0; j < num_paths; j++) {
> > > +               dbus_int32_t i_s = m2->tone_pct[j].i_sample;
> > > +               dbus_int32_t q_s = m2->tone_pct[j].q_sample;
> > > +
> > > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> > > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> > > +       }
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +
> > > +       /* toneQualityIndicators: "ay" */
> > > +       key = "toneQualityIndicators";
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                        &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> > > +       for (j = 0; j < num_paths; j++) {
> > > +               uint8_t tqi = m2->tone_quality_indicator[j];
> > > +
> > > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi);
> > > +       }
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void append_proc_enable_config(DBusMessageIter *outer_dict,
> > > +                               const struct rap_ev_cs_proc_enable_cmplt *cfg)
> > > +{
> > > +       DBusMessageIter entry, variant, inner;
> > > +       const char *key = "procedureEnableConfig";
> > > +       dbus_uint32_t sub_evt_len_us;
> > > +
> > > +       sub_evt_len_us = cfg->sub_evt_len[0] |
> > > +                        ((uint32_t)cfg->sub_evt_len[1] << 8) |
> > > +                        ((uint32_t)cfg->sub_evt_len[2] << 16);
> > > +
> > > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                        NULL, &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> > > +                                        &inner);
> > > +
> > > +       dict_append_byte(&inner, "toneAntennaConfigSelection",
> > > +                        cfg->tone_ant_config_sel);
> > > +       dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us);
> > > +       dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt);
> > > +       dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl);
> > > +       dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl);
> > > +       dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl);
> > > +       dict_append_uint32(&inner, "procedureCount", cfg->proc_counter);
> > > +       dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len);
> > > +
> > > +       dbus_message_iter_close_container(&variant, &inner);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(outer_dict, &entry);
> > > +}
> > > +
> > > +static void append_cs_config_param(DBusMessageIter *outer_dict,
> > > +                               const struct bcs_procedure_data *bcs)
> > > +{
> > > +       DBusMessageIter entry, variant, inner;
> > > +       const char *key = "csConfigParam";
> > > +       const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config;
> > > +
> > > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                        NULL, &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> > > +                                        &inner);
> > > +
> > > +       dict_append_byte(&inner, "modeType", cfg->main_mode_type);
> > > +       dict_append_byte(&inner, "subModeType", cfg->sub_mode_type);
> > > +       dict_append_byte(&inner, "rttType", cfg->rtt_type);
> > > +       dict_append_byte_array(&inner, "channelMap", cfg->channel_map,
> > > +                              sizeof(cfg->channel_map));
> > > +       dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps);
> > > +       dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps);
> > > +       dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep);
> > > +       dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps);
> > > +       dict_append_byte(&inner, "role", cfg->role);
> > > +       dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy);
> > > +       dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type);
> > > +       dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape);
> > > +       dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump);
> > > +       dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep);
> > > +       dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time);
> > > +       dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time);
> > > +       dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time);
> > > +       dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time);
> > > +       dict_append_byte(&inner, "tSwTimeUsSupportedByLocal",
> > > +                        bcs->t_sw_time_us_supported_by_local);
> > > +       dict_append_byte(&inner, "tSwTimeUsSupportedByRemote",
> > > +                        bcs->t_sw_time_us_supported_by_remote);
> > > +       dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval);
> > > +
> > > +       dbus_message_iter_close_container(&variant, &inner);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(outer_dict, &entry);
> > > +}
> > > +
> > > +static void append_step_to_dict(DBusMessageIter *dict,
> > > +                               const struct cs_step_data *step,
> > > +                               uint8_t num_ant_paths)
> > > +{
> > > +       DBusMessageIter entry, variant, inner;
> > > +       const char *mode_key;
> > > +
> > > +       dict_append_byte(dict, "stepMode", step->step_mode);
> > > +       dict_append_byte(dict, "stepChannel", step->step_chnl);
> > > +
> > > +       switch (step->step_mode) {
> > > +       case CS_MODE_ZERO:
> > > +               mode_key = "modeZeroData";
> > > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                                NULL, &entry);
> > > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > > +                                              &mode_key);
> > > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > > +                                                "a{sv}", &variant);
> > > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > > +                                                "{sv}", &inner);
> > > +               append_mode_zero(&inner,
> > > +                                &step->step_mode_data.mode_zero_data);
> > > +               dbus_message_iter_close_container(&variant, &inner);
> > > +               dbus_message_iter_close_container(&entry, &variant);
> > > +               dbus_message_iter_close_container(dict, &entry);
> > > +               break;
> > > +
> > > +       case CS_MODE_ONE:
> > > +               mode_key = "modeOneData";
> > > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                                NULL, &entry);
> > > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > > +                                              &mode_key);
> > > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > > +                                                "a{sv}", &variant);
> > > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > > +                                                "{sv}", &inner);
> > > +               append_mode_one(&inner,
> > > +                               &step->step_mode_data.mode_one_data);
> > > +               dbus_message_iter_close_container(&variant, &inner);
> > > +               dbus_message_iter_close_container(&entry, &variant);
> > > +               dbus_message_iter_close_container(dict, &entry);
> > > +               break;
> > > +
> > > +       case CS_MODE_TWO:
> > > +               mode_key = "modeTwoData";
> > > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                                NULL, &entry);
> > > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > > +                                              &mode_key);
> > > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > > +                                                "a{sv}", &variant);
> > > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > > +                                                "{sv}", &inner);
> > > +               append_mode_two(&inner,
> > > +                               &step->step_mode_data.mode_two_data,
> > > +                               num_ant_paths);
> > > +               dbus_message_iter_close_container(&variant, &inner);
> > > +               dbus_message_iter_close_container(&entry, &variant);
> > > +               dbus_message_iter_close_container(dict, &entry);
> > > +               break;
> > > +
> > > +       case CS_MODE_THREE:
> > > +               mode_key = "modeThreeData";
> > > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                                NULL, &entry);
> > > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > > +                                              &mode_key);
> > > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > > +                                                "a{sv}", &variant);
> > > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > > +                                                "{sv}", &inner);
> > > +               append_mode_one(&inner,
> > > +                       &step->step_mode_data.mode_three_data.mode_one_data);
> > > +               append_mode_two(&inner,
> > > +                       &step->step_mode_data.mode_three_data.mode_two_data,
> > > +                       num_ant_paths);
> > > +               dbus_message_iter_close_container(&variant, &inner);
> > > +               dbus_message_iter_close_container(&entry, &variant);
> > > +               dbus_message_iter_close_container(dict, &entry);
> > > +               break;
> > > +
> > > +       default:
> > > +               break;
> > > +       }
> > > +}
> > > +
> > > +static void append_subevent_to_dict(DBusMessageIter *dict,
> > > +                               const struct cs_subevent_result_data *sub)
> > > +{
> > > +       DBusMessageIter entry, variant, arr;
> > > +       const char *key;
> > > +       uint32_t i;
> > > +
> > > +       dict_append_int32(dict, "startAclConnEvtCounter",
> > > +                         (dbus_int32_t)sub->start_acl_conn_evt_counter);
> > > +       dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp);
> > > +       dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl);
> > > +       dict_append_byte(dict, "numAntPaths", sub->num_ant_paths);
> > > +       dict_append_byte(dict, "subeventAbortReason",
> > > +                        sub->subevent_abort_reason);
> > > +       dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos);
> > > +       dict_append_uint32(dict, "numSteps", sub->num_steps);
> > > +
> > > +       /* stepData: av (each element is a{sv}) */
> > > +       key = "stepData";
> > > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > > +                                        &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> > > +       if (sub->step_data) {
> > > +               for (i = 0; i < sub->num_steps; i++) {
> > > +                       DBusMessageIter inner, step_dict;
> > > +
> > > +                       dbus_message_iter_open_container(&arr,
> > > +                                       DBUS_TYPE_VARIANT, "a{sv}", &inner);
> > > +                       dbus_message_iter_open_container(&inner,
> > > +                                       DBUS_TYPE_ARRAY, "{sv}", &step_dict);
> > > +                       append_step_to_dict(&step_dict, &sub->step_data[i],
> > > +                                           sub->num_ant_paths);
> > > +                       dbus_message_iter_close_container(&inner, &step_dict);
> > > +                       dbus_message_iter_close_container(&arr, &inner);
> > > +               }
> > > +       }
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(dict, &entry);
> > > +}
> > > +
> > > +static void append_subevent_array(DBusMessageIter *outer_dict,
> > > +                               const char *key,
> > > +                               const struct cs_subevent_result_data *subevents,
> > > +                               uint32_t count)
> > > +{
> > > +       DBusMessageIter entry, variant, arr;
> > > +       uint32_t i;
> > > +
> > > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > > +                                        NULL, &entry);
> > > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> > > +                                        &variant);
> > > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> > > +       for (i = 0; i < count; i++) {
> > > +               DBusMessageIter inner, sub_dict;
> > > +
> > > +               dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT,
> > > +                                                "a{sv}", &inner);
> > > +               dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY,
> > > +                                                "{sv}", &sub_dict);
> > > +               append_subevent_to_dict(&sub_dict, &subevents[i]);
> > > +               dbus_message_iter_close_container(&inner, &sub_dict);
> > > +               dbus_message_iter_close_container(&arr, &inner);
> > > +       }
> > > +       dbus_message_iter_close_container(&variant, &arr);
> > > +       dbus_message_iter_close_container(&entry, &variant);
> > > +       dbus_message_iter_close_container(outer_dict, &entry);
> > > +}
> > > +
> > > +static void rap_emit_procedure_data(struct rap_data *data,
> > > +                               const struct bcs_procedure_data *bcs)
> > > +{
> > > +       DBusMessage *signal;
> > > +       DBusMessageIter iter, dict;
> > > +
> > > +       signal = dbus_message_new_signal(device_get_path(data->device),
> > > +                                        CS_INTERFACE, "ProcedureData");
> > > +       if (!signal) {
> > > +               error("Failed to allocate ProcedureData signal");
> > > +               return;
> > > +       }
> > > +
> > > +       dbus_message_iter_init_append(signal, &iter);
> > > +       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
> > > +
> > > +       dict_append_int32(&dict, "procedureCounter",
> > > +                         (dbus_int32_t)bcs->procedure_counter);
> > > +       dict_append_int32(&dict, "procedureSequence",
> > > +                         (dbus_int32_t)bcs->procedure_sequence);
> > > +       dict_append_byte(&dict, "initiatorSelectedTxPower",
> > > +                        (uint8_t)bcs->initiator_selected_tx_power);
> > > +       dict_append_byte(&dict, "reflectorSelectedTxPower",
> > > +                        (uint8_t)bcs->reflector_selected_tx_power);
> > > +
> > > +       dict_append_uint32(&dict, "initiatorSubeventCount",
> > > +                          bcs->initiator_subevent_count);
> > > +       if (bcs->initiator_subevent_results &&
> > > +                                       bcs->initiator_subevent_count > 0)
> > > +               append_subevent_array(&dict, "initiatorSubeventResults",
> > > +                                     bcs->initiator_subevent_results,
> > > +                                     bcs->initiator_subevent_count);
> > > +
> > > +       dict_append_byte(&dict, "initiatorProcedureAbortReason",
> > > +                        bcs->initiator_procedure_abort_reason);
> > > +
> > > +       dict_append_uint32(&dict, "reflectorSubeventCount",
> > > +                          bcs->reflector_subevent_count);
> > > +       if (bcs->reflector_subevent_results &&
> > > +                                       bcs->reflector_subevent_count > 0)
> > > +               append_subevent_array(&dict, "reflectorSubeventResults",
> > > +                                     bcs->reflector_subevent_results,
> > > +                                     bcs->reflector_subevent_count);
> > > +
> > > +       dict_append_byte(&dict, "reflectorProcedureAbortReason",
> > > +                        bcs->reflector_procedure_abort_reason);
> > > +
> > > +       append_proc_enable_config(&dict, &bcs->proc_enable_config);
> > > +       append_cs_config_param(&dict, bcs);
> > > +
> > > +       dbus_message_iter_close_container(&iter, &dict);
> > > +
> > > +       g_dbus_send_message(btd_get_dbus_connection(), signal);
> > > +}
> > > +
> > > +static void rap_procedure_data(struct bt_rap *rap,
> > > +                               struct bcs_procedure_data *bcs,
> > > +                               void *user_data)
> > > +{
> > > +       struct rap_data *data = user_data;
> > > +
> > > +       DBG("procedure_counter=%u", bcs->procedure_counter);
> > > +       rap_emit_procedure_data(data, bcs);
> > > +}
> > > +
> > >  static DBusMessage *start_measurement(DBusConnection *conn,
> > >                                 DBusMessage *msg, void *user_data)
> > >  {
> > > @@ -534,6 +1020,9 @@ bad_type:
> > >         data->active_session.cfg           = cfg;
> > >         data->active_session.freq          = freq;
> > >
> > > +       bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data,
> > > +                                        data, NULL);
> > > +
> > >         return dbus_message_new_method_return(msg);
> > >  }
> > >
> > > @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = {
> > >         { }
> > >  };
> > >
> > > +static const GDBusSignalTable cs_dbus_signals[] = {
> > > +       { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) },
> > > +       { }
> > > +};
> > > +
> > >  static void rap_measurement_timeout_cb(void *user_data)
> > >  {
> > >         struct rap_data *data = user_data;
> > > @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service)
> > >         g_dbus_register_interface(btd_get_dbus_connection(),
> > >                                   device_get_path(data->device),
> > >                                   CS_INTERFACE, cs_dbus_methods,
> > > -                                 NULL, cs_dbus_properties, data, NULL);
> > > +                                 cs_dbus_signals, cs_dbus_properties,
> > > +                                 data, NULL);
> > >
> > >         return 0;
> > >  }
> > > diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
> > > index e57f967a2..3da98972e 100644
> > > --- a/profiles/ranging/rap_hci.c
> > > +++ b/profiles/ranging/rap_hci.c
> > > @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
> > >         DBG("Sending read remote capabilities for handle 0x%04X",
> > >                 sm->active_conn_handle);
> > >         bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle);
> > > +
> > > +       bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported);
> > >  }
> > >
> > >  static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
> > > @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
> > >                 cs_set_state(sm, CS_STATE_INIT);
> > >                 rap_send_hci_def_settings_command(sm, evt);
> > >         }
> > > +
> > > +       bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported);
> > >  }
> > >
> > >  static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
> > > @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
> > >                         &rap_ev, sm->rap);
> > >  }
> > >
> > > +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size,
> > > +                                           void *user_data)
> > > +{
> > > +       struct cs_state_machine *sm = user_data;
> > > +       const struct bt_hci_evt_le_conn_update_complete *evt;
> > > +       struct rap_conn_mapping *mapping;
> > > +       struct bt_rap *rap;
> > > +       struct iovec iov;
> > > +
> > > +       if (!sm || !data ||
> > > +           size < sizeof(struct bt_hci_evt_le_conn_update_complete))
> > > +               return;
> > > +
> > > +       iov.iov_base = (void *) data;
> > > +       iov.iov_len = size;
> > > +
> > > +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
> > > +       if (!evt) {
> > > +               error("Failed to pull LE conn update complete struct");
> > > +               return;
> > > +       }
> > > +
> > > +       DBG("status=0x%02X handle=0x%04X interval=%u",
> > > +           evt->status, evt->handle, evt->interval);
> > > +
> > > +       if (evt->status != 0)
> > > +               return;
> > > +
> > > +       mapping = find_mapping_by_handle(sm, evt->handle);
> > > +       if (mapping && mapping->rap) {
> > > +               DBG("Found handle 0x%04X in mapping cache", evt->handle);
> > > +               rap = mapping->rap;
> > > +       } else {
> > > +               error("No mapping found for handle 0x%04X", evt->handle);
> > > +               return;
> > > +       }
> > > +
> > > +       bt_rap_set_conn_interval(rap, evt->interval);
> > > +}
> > > +
> > >  static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
> > >                                 int16_t *q_sample)
> > >  {
> > > @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
> > >                                         rap_cs_subevt_result_evt },
> > >                 { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE,
> > >                                         rap_cs_subevt_result_cont_evt },
> > > +               { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE,
> > > +                                       rap_le_conn_update_complete_evt },
> > >         };
> > >         struct cs_state_machine *sm;
> > >         unsigned int i;
> > > @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm)
> > >                                                 false);
> > >  }
> > >
> > > +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm,
> > > +                               bt_rap_procedure_data_func_t cb,
> > > +                               void *user_data,
> > > +                               bt_rap_destroy_func_t destroy)
> > > +{
> > > +       struct cs_state_machine *sm = hci_sm;
> > > +
> > > +       if (!sm || !sm->rap)
> > > +               return false;
> > > +
> > > +       return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy);
> > > +}
> > > +
> > >  bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap,
> > >                 uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
> > >                 bool is_central)
> > > --
> > > 2.34.1
> > >
> >
> >
> > --
> > Luiz Augusto von Dentz
>
>
>
> --
> Luiz Augusto von Dentz



-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH] Bluetooth: HIDP: add missing length check for incoming frames
From: Luiz Augusto von Dentz @ 2026-07-17 15:40 UTC (permalink / raw)
  To: Jiale Yao
  Cc: Marcel Holtmann, Tim Bird, Muhammad Bilal, Kees Cook,
	Michael Bommarito, linux-bluetooth, linux-kernel
In-Reply-To: <20260717153238.2002330-1-yaojiale02@163.com>

Hi,

On Fri, Jul 17, 2026 at 11:33 AM Jiale Yao <yaojiale02@163.com> wrote:
>
> In hidp_recv_ctrl_frame() and hidp_recv_intr_frame(), skb->data[0] is
> read without verifying that skb->len >= 1.  A zero-length L2CAP PDU
> delivered via the HIDP control or interrupt channel causes an
> out-of-bounds read.
>
> Add pskb_may_pull(skb, 1) guards before both reads, matching the fix
> in commit 6770d3a8acdf ("Bluetooth: bnep: reject short frames before
> parsing") which addressed the same class of bug in BNEP.
>
> Assisted-by: Claude:deepseek-v4-pro
> Signed-off-by: Jiale Yao <yaojiale02@163.com>
> ---
>  net/bluetooth/hidp/core.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
> index 0e24c5e2955e..6c7da8aca732 100644
> --- a/net/bluetooth/hidp/core.c
> +++ b/net/bluetooth/hidp/core.c
> @@ -565,6 +565,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
>
>         BT_DBG("session %p skb %p len %u", session, skb, skb->len);
>
> +       if (!pskb_may_pull(skb, 1))
> +               return;

We can probably replace this with skb_pull_data.

>         hdr = skb->data[0];
>         skb_pull(skb, 1);
>
> @@ -601,6 +603,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
>
>         BT_DBG("session %p skb %p len %u", session, skb, skb->len);
>
> +       if (!pskb_may_pull(skb, 1))
> +               return;

Ditto.

>         hdr = skb->data[0];
>         skb_pull(skb, 1);
>
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* [PATCH] Bluetooth: HIDP: add missing length check for incoming frames
From: Jiale Yao @ 2026-07-17 15:32 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Jiale Yao, Tim Bird,
	Muhammad Bilal, Kees Cook, Michael Bommarito, linux-bluetooth,
	linux-kernel

In hidp_recv_ctrl_frame() and hidp_recv_intr_frame(), skb->data[0] is
read without verifying that skb->len >= 1.  A zero-length L2CAP PDU
delivered via the HIDP control or interrupt channel causes an
out-of-bounds read.

Add pskb_may_pull(skb, 1) guards before both reads, matching the fix
in commit 6770d3a8acdf ("Bluetooth: bnep: reject short frames before
parsing") which addressed the same class of bug in BNEP.

Assisted-by: Claude:deepseek-v4-pro
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
 net/bluetooth/hidp/core.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 0e24c5e2955e..6c7da8aca732 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -565,6 +565,8 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
 
 	BT_DBG("session %p skb %p len %u", session, skb, skb->len);
 
+	if (!pskb_may_pull(skb, 1))
+		return;
 	hdr = skb->data[0];
 	skb_pull(skb, 1);
 
@@ -601,6 +603,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
 
 	BT_DBG("session %p skb %p len %u", session, skb, skb->len);
 
+	if (!pskb_may_pull(skb, 1))
+		return;
 	hdr = skb->data[0];
 	skb_pull(skb, 1);
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding
From: Luiz Augusto von Dentz @ 2026-07-17 15:17 UTC (permalink / raw)
  To: Prathibha Madugonde
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <CABBYNZ++i-JNcjCz5PG5JKNf9aWgMkm05i4-m7b5TPhTdMmojg@mail.gmail.com>

Hi.

On Fri, Jul 17, 2026 at 11:07 AM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi Prathibha,
>
> On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde
> <prathibha.madugonde@oss.qualcomm.com> wrote:
> >
> > From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
> >
> > Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results,
> > and CS/procedure-enable config) into an a{sv} dictionary and emit it
> > as a new ProcedureData signal on the CS D-Bus interface.
>
> Was this documented though? It doesn't seem to follow the likes of
> https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst,
> for instance we don't use camel case for dictionary options. Also, if
> this is a result of `StartMeasurement`, we should probably signal it
> as Results or something like that, but I'd start with documentation so
> we can agree on the overall design.

Ok, looks like this does follow the documentation already:

https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst#void-proceduredatadict-data

That said that will probably need updating since some dictionaries use
camel case and others don't, so this needs to converge to a single
format.

> > Register a procedure_data callback on the RAP HCI state machine so
> > rap.c is notified when a procedure completes, and propagate the
> > locally supported T_SW time capability from the read-local-supported-
> > capabilities response into bt_rap.
> >
> > ---
> >  profiles/ranging/rap.c     | 497 ++++++++++++++++++++++++++++++++++++-
> >  profiles/ranging/rap_hci.c |  59 +++++
> >  2 files changed, 555 insertions(+), 1 deletion(-)
> >
> > diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
> > index 3ffc0da76..42e1ef2a4 100644
> > --- a/profiles/ranging/rap.c
> > +++ b/profiles/ranging/rap.c
> > @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc(
> >         return NULL;
> >  }
> >
> > +static void dict_append_byte(DBusMessageIter *dict, const char *key,
> > +                               uint8_t val)
> > +{
> > +       DBusMessageIter entry, variant;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                               &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y",
> > +                                               &variant);
> > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void dict_append_int32(DBusMessageIter *dict, const char *key,
> > +                               dbus_int32_t val)
> > +{
> > +       DBusMessageIter entry, variant;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                               &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i",
> > +                                               &variant);
> > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void dict_append_uint32(DBusMessageIter *dict, const char *key,
> > +                               dbus_uint32_t val)
> > +{
> > +       DBusMessageIter entry, variant;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                               &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u",
> > +                                               &variant);
> > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void dict_append_uint64(DBusMessageIter *dict, const char *key,
> > +                               dbus_uint64_t val)
> > +{
> > +       DBusMessageIter entry, variant;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                               &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t",
> > +                                               &variant);
> > +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void dict_append_byte_array(DBusMessageIter *dict, const char *key,
> > +                                       const uint8_t *data, int len)
> > +{
> > +       DBusMessageIter entry, variant, arr;
> > +       int i;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                               &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> > +                                               &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> > +       for (i = 0; i < len; i++)
> > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]);
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void append_mode_zero(DBusMessageIter *dict,
> > +                               const struct cs_mode_zero_data *m0)
> > +{
> > +       dict_append_byte(dict, "packetQuality", m0->packet_quality);
> > +       dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm);
> > +       dict_append_byte(dict, "packetAntenna", m0->packet_ant);
> > +       dict_append_int32(dict, "initiatorMeasuredFreqOffset",
> > +                               (dbus_int32_t)m0->init_measured_freq_offset);
> > +}
> > +
> > +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key,
> > +                               const struct pct_iq_sample *pct)
> > +{
> > +       DBusMessageIter entry, variant, arr;
> > +       dbus_int32_t i_s = pct->i_sample;
> > +       dbus_int32_t q_s = pct->q_sample;
> > +
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                        &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> > +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> > +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void append_mode_one(DBusMessageIter *dict,
> > +                               const struct cs_mode_one_data *m1)
> > +{
> > +       dict_append_byte(dict, "packetQuality", m1->packet_quality);
> > +       dict_append_byte(dict, "packetNadm", m1->packet_nadm);
> > +       dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm);
> > +       dict_append_int32(dict, "toaTodInitiator",
> > +                               (dbus_int32_t)m1->toa_tod_init);
> > +       dict_append_int32(dict, "todToaReflector",
> > +                               (dbus_int32_t)m1->tod_toa_refl);
> > +       dict_append_byte(dict, "packetAntenna", m1->packet_ant);
> > +       append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1);
> > +       append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2);
> > +}
> > +
> > +static void append_mode_two(DBusMessageIter *dict,
> > +                               const struct cs_mode_two_data *m2,
> > +                               uint8_t num_ant_paths)
> > +{
> > +       DBusMessageIter entry, variant, arr;
> > +       const char *key;
> > +       int j;
> > +       int num_paths;
> > +
> > +       /*
> > +        * num_ant_paths is the HCI "number of antenna paths" value
> > +        * (0-indexed), so actual tone sample count = num_ant_paths + 1,
> > +        * capped at array size.
> > +        */
> > +       num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ?
> > +                               (num_ant_paths + 1) : CS_MAX_ANT_PATHS;
> > +
> > +       dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index);
> > +
> > +       /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */
> > +       key = "tonePctIQSamples";
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                        &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> > +       for (j = 0; j < num_paths; j++) {
> > +               dbus_int32_t i_s = m2->tone_pct[j].i_sample;
> > +               dbus_int32_t q_s = m2->tone_pct[j].q_sample;
> > +
> > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> > +       }
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +
> > +       /* toneQualityIndicators: "ay" */
> > +       key = "toneQualityIndicators";
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                        &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> > +       for (j = 0; j < num_paths; j++) {
> > +               uint8_t tqi = m2->tone_quality_indicator[j];
> > +
> > +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi);
> > +       }
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void append_proc_enable_config(DBusMessageIter *outer_dict,
> > +                               const struct rap_ev_cs_proc_enable_cmplt *cfg)
> > +{
> > +       DBusMessageIter entry, variant, inner;
> > +       const char *key = "procedureEnableConfig";
> > +       dbus_uint32_t sub_evt_len_us;
> > +
> > +       sub_evt_len_us = cfg->sub_evt_len[0] |
> > +                        ((uint32_t)cfg->sub_evt_len[1] << 8) |
> > +                        ((uint32_t)cfg->sub_evt_len[2] << 16);
> > +
> > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > +                                        NULL, &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> > +                                        &inner);
> > +
> > +       dict_append_byte(&inner, "toneAntennaConfigSelection",
> > +                        cfg->tone_ant_config_sel);
> > +       dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us);
> > +       dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt);
> > +       dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl);
> > +       dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl);
> > +       dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl);
> > +       dict_append_uint32(&inner, "procedureCount", cfg->proc_counter);
> > +       dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len);
> > +
> > +       dbus_message_iter_close_container(&variant, &inner);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(outer_dict, &entry);
> > +}
> > +
> > +static void append_cs_config_param(DBusMessageIter *outer_dict,
> > +                               const struct bcs_procedure_data *bcs)
> > +{
> > +       DBusMessageIter entry, variant, inner;
> > +       const char *key = "csConfigParam";
> > +       const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config;
> > +
> > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > +                                        NULL, &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> > +                                        &inner);
> > +
> > +       dict_append_byte(&inner, "modeType", cfg->main_mode_type);
> > +       dict_append_byte(&inner, "subModeType", cfg->sub_mode_type);
> > +       dict_append_byte(&inner, "rttType", cfg->rtt_type);
> > +       dict_append_byte_array(&inner, "channelMap", cfg->channel_map,
> > +                              sizeof(cfg->channel_map));
> > +       dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps);
> > +       dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps);
> > +       dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep);
> > +       dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps);
> > +       dict_append_byte(&inner, "role", cfg->role);
> > +       dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy);
> > +       dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type);
> > +       dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape);
> > +       dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump);
> > +       dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep);
> > +       dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time);
> > +       dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time);
> > +       dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time);
> > +       dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time);
> > +       dict_append_byte(&inner, "tSwTimeUsSupportedByLocal",
> > +                        bcs->t_sw_time_us_supported_by_local);
> > +       dict_append_byte(&inner, "tSwTimeUsSupportedByRemote",
> > +                        bcs->t_sw_time_us_supported_by_remote);
> > +       dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval);
> > +
> > +       dbus_message_iter_close_container(&variant, &inner);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(outer_dict, &entry);
> > +}
> > +
> > +static void append_step_to_dict(DBusMessageIter *dict,
> > +                               const struct cs_step_data *step,
> > +                               uint8_t num_ant_paths)
> > +{
> > +       DBusMessageIter entry, variant, inner;
> > +       const char *mode_key;
> > +
> > +       dict_append_byte(dict, "stepMode", step->step_mode);
> > +       dict_append_byte(dict, "stepChannel", step->step_chnl);
> > +
> > +       switch (step->step_mode) {
> > +       case CS_MODE_ZERO:
> > +               mode_key = "modeZeroData";
> > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > +                                                NULL, &entry);
> > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > +                                              &mode_key);
> > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > +                                                "a{sv}", &variant);
> > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > +                                                "{sv}", &inner);
> > +               append_mode_zero(&inner,
> > +                                &step->step_mode_data.mode_zero_data);
> > +               dbus_message_iter_close_container(&variant, &inner);
> > +               dbus_message_iter_close_container(&entry, &variant);
> > +               dbus_message_iter_close_container(dict, &entry);
> > +               break;
> > +
> > +       case CS_MODE_ONE:
> > +               mode_key = "modeOneData";
> > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > +                                                NULL, &entry);
> > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > +                                              &mode_key);
> > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > +                                                "a{sv}", &variant);
> > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > +                                                "{sv}", &inner);
> > +               append_mode_one(&inner,
> > +                               &step->step_mode_data.mode_one_data);
> > +               dbus_message_iter_close_container(&variant, &inner);
> > +               dbus_message_iter_close_container(&entry, &variant);
> > +               dbus_message_iter_close_container(dict, &entry);
> > +               break;
> > +
> > +       case CS_MODE_TWO:
> > +               mode_key = "modeTwoData";
> > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > +                                                NULL, &entry);
> > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > +                                              &mode_key);
> > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > +                                                "a{sv}", &variant);
> > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > +                                                "{sv}", &inner);
> > +               append_mode_two(&inner,
> > +                               &step->step_mode_data.mode_two_data,
> > +                               num_ant_paths);
> > +               dbus_message_iter_close_container(&variant, &inner);
> > +               dbus_message_iter_close_container(&entry, &variant);
> > +               dbus_message_iter_close_container(dict, &entry);
> > +               break;
> > +
> > +       case CS_MODE_THREE:
> > +               mode_key = "modeThreeData";
> > +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> > +                                                NULL, &entry);
> > +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> > +                                              &mode_key);
> > +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> > +                                                "a{sv}", &variant);
> > +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> > +                                                "{sv}", &inner);
> > +               append_mode_one(&inner,
> > +                       &step->step_mode_data.mode_three_data.mode_one_data);
> > +               append_mode_two(&inner,
> > +                       &step->step_mode_data.mode_three_data.mode_two_data,
> > +                       num_ant_paths);
> > +               dbus_message_iter_close_container(&variant, &inner);
> > +               dbus_message_iter_close_container(&entry, &variant);
> > +               dbus_message_iter_close_container(dict, &entry);
> > +               break;
> > +
> > +       default:
> > +               break;
> > +       }
> > +}
> > +
> > +static void append_subevent_to_dict(DBusMessageIter *dict,
> > +                               const struct cs_subevent_result_data *sub)
> > +{
> > +       DBusMessageIter entry, variant, arr;
> > +       const char *key;
> > +       uint32_t i;
> > +
> > +       dict_append_int32(dict, "startAclConnEvtCounter",
> > +                         (dbus_int32_t)sub->start_acl_conn_evt_counter);
> > +       dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp);
> > +       dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl);
> > +       dict_append_byte(dict, "numAntPaths", sub->num_ant_paths);
> > +       dict_append_byte(dict, "subeventAbortReason",
> > +                        sub->subevent_abort_reason);
> > +       dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos);
> > +       dict_append_uint32(dict, "numSteps", sub->num_steps);
> > +
> > +       /* stepData: av (each element is a{sv}) */
> > +       key = "stepData";
> > +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> > +                                        &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> > +       if (sub->step_data) {
> > +               for (i = 0; i < sub->num_steps; i++) {
> > +                       DBusMessageIter inner, step_dict;
> > +
> > +                       dbus_message_iter_open_container(&arr,
> > +                                       DBUS_TYPE_VARIANT, "a{sv}", &inner);
> > +                       dbus_message_iter_open_container(&inner,
> > +                                       DBUS_TYPE_ARRAY, "{sv}", &step_dict);
> > +                       append_step_to_dict(&step_dict, &sub->step_data[i],
> > +                                           sub->num_ant_paths);
> > +                       dbus_message_iter_close_container(&inner, &step_dict);
> > +                       dbus_message_iter_close_container(&arr, &inner);
> > +               }
> > +       }
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(dict, &entry);
> > +}
> > +
> > +static void append_subevent_array(DBusMessageIter *outer_dict,
> > +                               const char *key,
> > +                               const struct cs_subevent_result_data *subevents,
> > +                               uint32_t count)
> > +{
> > +       DBusMessageIter entry, variant, arr;
> > +       uint32_t i;
> > +
> > +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> > +                                        NULL, &entry);
> > +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> > +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> > +                                        &variant);
> > +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> > +       for (i = 0; i < count; i++) {
> > +               DBusMessageIter inner, sub_dict;
> > +
> > +               dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT,
> > +                                                "a{sv}", &inner);
> > +               dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY,
> > +                                                "{sv}", &sub_dict);
> > +               append_subevent_to_dict(&sub_dict, &subevents[i]);
> > +               dbus_message_iter_close_container(&inner, &sub_dict);
> > +               dbus_message_iter_close_container(&arr, &inner);
> > +       }
> > +       dbus_message_iter_close_container(&variant, &arr);
> > +       dbus_message_iter_close_container(&entry, &variant);
> > +       dbus_message_iter_close_container(outer_dict, &entry);
> > +}
> > +
> > +static void rap_emit_procedure_data(struct rap_data *data,
> > +                               const struct bcs_procedure_data *bcs)
> > +{
> > +       DBusMessage *signal;
> > +       DBusMessageIter iter, dict;
> > +
> > +       signal = dbus_message_new_signal(device_get_path(data->device),
> > +                                        CS_INTERFACE, "ProcedureData");
> > +       if (!signal) {
> > +               error("Failed to allocate ProcedureData signal");
> > +               return;
> > +       }
> > +
> > +       dbus_message_iter_init_append(signal, &iter);
> > +       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
> > +
> > +       dict_append_int32(&dict, "procedureCounter",
> > +                         (dbus_int32_t)bcs->procedure_counter);
> > +       dict_append_int32(&dict, "procedureSequence",
> > +                         (dbus_int32_t)bcs->procedure_sequence);
> > +       dict_append_byte(&dict, "initiatorSelectedTxPower",
> > +                        (uint8_t)bcs->initiator_selected_tx_power);
> > +       dict_append_byte(&dict, "reflectorSelectedTxPower",
> > +                        (uint8_t)bcs->reflector_selected_tx_power);
> > +
> > +       dict_append_uint32(&dict, "initiatorSubeventCount",
> > +                          bcs->initiator_subevent_count);
> > +       if (bcs->initiator_subevent_results &&
> > +                                       bcs->initiator_subevent_count > 0)
> > +               append_subevent_array(&dict, "initiatorSubeventResults",
> > +                                     bcs->initiator_subevent_results,
> > +                                     bcs->initiator_subevent_count);
> > +
> > +       dict_append_byte(&dict, "initiatorProcedureAbortReason",
> > +                        bcs->initiator_procedure_abort_reason);
> > +
> > +       dict_append_uint32(&dict, "reflectorSubeventCount",
> > +                          bcs->reflector_subevent_count);
> > +       if (bcs->reflector_subevent_results &&
> > +                                       bcs->reflector_subevent_count > 0)
> > +               append_subevent_array(&dict, "reflectorSubeventResults",
> > +                                     bcs->reflector_subevent_results,
> > +                                     bcs->reflector_subevent_count);
> > +
> > +       dict_append_byte(&dict, "reflectorProcedureAbortReason",
> > +                        bcs->reflector_procedure_abort_reason);
> > +
> > +       append_proc_enable_config(&dict, &bcs->proc_enable_config);
> > +       append_cs_config_param(&dict, bcs);
> > +
> > +       dbus_message_iter_close_container(&iter, &dict);
> > +
> > +       g_dbus_send_message(btd_get_dbus_connection(), signal);
> > +}
> > +
> > +static void rap_procedure_data(struct bt_rap *rap,
> > +                               struct bcs_procedure_data *bcs,
> > +                               void *user_data)
> > +{
> > +       struct rap_data *data = user_data;
> > +
> > +       DBG("procedure_counter=%u", bcs->procedure_counter);
> > +       rap_emit_procedure_data(data, bcs);
> > +}
> > +
> >  static DBusMessage *start_measurement(DBusConnection *conn,
> >                                 DBusMessage *msg, void *user_data)
> >  {
> > @@ -534,6 +1020,9 @@ bad_type:
> >         data->active_session.cfg           = cfg;
> >         data->active_session.freq          = freq;
> >
> > +       bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data,
> > +                                        data, NULL);
> > +
> >         return dbus_message_new_method_return(msg);
> >  }
> >
> > @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = {
> >         { }
> >  };
> >
> > +static const GDBusSignalTable cs_dbus_signals[] = {
> > +       { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) },
> > +       { }
> > +};
> > +
> >  static void rap_measurement_timeout_cb(void *user_data)
> >  {
> >         struct rap_data *data = user_data;
> > @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service)
> >         g_dbus_register_interface(btd_get_dbus_connection(),
> >                                   device_get_path(data->device),
> >                                   CS_INTERFACE, cs_dbus_methods,
> > -                                 NULL, cs_dbus_properties, data, NULL);
> > +                                 cs_dbus_signals, cs_dbus_properties,
> > +                                 data, NULL);
> >
> >         return 0;
> >  }
> > diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
> > index e57f967a2..3da98972e 100644
> > --- a/profiles/ranging/rap_hci.c
> > +++ b/profiles/ranging/rap_hci.c
> > @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
> >         DBG("Sending read remote capabilities for handle 0x%04X",
> >                 sm->active_conn_handle);
> >         bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle);
> > +
> > +       bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported);
> >  }
> >
> >  static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
> > @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
> >                 cs_set_state(sm, CS_STATE_INIT);
> >                 rap_send_hci_def_settings_command(sm, evt);
> >         }
> > +
> > +       bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported);
> >  }
> >
> >  static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
> > @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
> >                         &rap_ev, sm->rap);
> >  }
> >
> > +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size,
> > +                                           void *user_data)
> > +{
> > +       struct cs_state_machine *sm = user_data;
> > +       const struct bt_hci_evt_le_conn_update_complete *evt;
> > +       struct rap_conn_mapping *mapping;
> > +       struct bt_rap *rap;
> > +       struct iovec iov;
> > +
> > +       if (!sm || !data ||
> > +           size < sizeof(struct bt_hci_evt_le_conn_update_complete))
> > +               return;
> > +
> > +       iov.iov_base = (void *) data;
> > +       iov.iov_len = size;
> > +
> > +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
> > +       if (!evt) {
> > +               error("Failed to pull LE conn update complete struct");
> > +               return;
> > +       }
> > +
> > +       DBG("status=0x%02X handle=0x%04X interval=%u",
> > +           evt->status, evt->handle, evt->interval);
> > +
> > +       if (evt->status != 0)
> > +               return;
> > +
> > +       mapping = find_mapping_by_handle(sm, evt->handle);
> > +       if (mapping && mapping->rap) {
> > +               DBG("Found handle 0x%04X in mapping cache", evt->handle);
> > +               rap = mapping->rap;
> > +       } else {
> > +               error("No mapping found for handle 0x%04X", evt->handle);
> > +               return;
> > +       }
> > +
> > +       bt_rap_set_conn_interval(rap, evt->interval);
> > +}
> > +
> >  static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
> >                                 int16_t *q_sample)
> >  {
> > @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
> >                                         rap_cs_subevt_result_evt },
> >                 { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE,
> >                                         rap_cs_subevt_result_cont_evt },
> > +               { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE,
> > +                                       rap_le_conn_update_complete_evt },
> >         };
> >         struct cs_state_machine *sm;
> >         unsigned int i;
> > @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm)
> >                                                 false);
> >  }
> >
> > +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm,
> > +                               bt_rap_procedure_data_func_t cb,
> > +                               void *user_data,
> > +                               bt_rap_destroy_func_t destroy)
> > +{
> > +       struct cs_state_machine *sm = hci_sm;
> > +
> > +       if (!sm || !sm->rap)
> > +               return false;
> > +
> > +       return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy);
> > +}
> > +
> >  bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap,
> >                 uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
> >                 bool is_central)
> > --
> > 2.34.1
> >
>
>
> --
> Luiz Augusto von Dentz



-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding
From: Luiz Augusto von Dentz @ 2026-07-17 15:07 UTC (permalink / raw)
  To: Prathibha Madugonde
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260717072647.255002-3-prathm@qti.qualcomm.com>

Hi Prathibha,

On Fri, Jul 17, 2026 at 3:27 AM Prathibha Madugonde
<prathibha.madugonde@oss.qualcomm.com> wrote:
>
> From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
>
> Marshal bcs_procedure_data (subevents, steps, mode 0/1/2/3 results,
> and CS/procedure-enable config) into an a{sv} dictionary and emit it
> as a new ProcedureData signal on the CS D-Bus interface.

Was this documented though? It doesn't seem to follow the likes of
https://github.com/bluez/bluez/blob/master/doc/org.bluez.ChannelSounding1.rst,
for instance we don't use camel case for dictionary options. Also, if
this is a result of `StartMeasurement`, we should probably signal it
as Results or something like that, but I'd start with documentation so
we can agree on the overall design.

> Register a procedure_data callback on the RAP HCI state machine so
> rap.c is notified when a procedure completes, and propagate the
> locally supported T_SW time capability from the read-local-supported-
> capabilities response into bt_rap.
>
> ---
>  profiles/ranging/rap.c     | 497 ++++++++++++++++++++++++++++++++++++-
>  profiles/ranging/rap_hci.c |  59 +++++
>  2 files changed, 555 insertions(+), 1 deletion(-)
>
> diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
> index 3ffc0da76..42e1ef2a4 100644
> --- a/profiles/ranging/rap.c
> +++ b/profiles/ranging/rap.c
> @@ -379,6 +379,492 @@ static const struct cs_dict_param_desc *cs_find_dict_param_desc(
>         return NULL;
>  }
>
> +static void dict_append_byte(DBusMessageIter *dict, const char *key,
> +                               uint8_t val)
> +{
> +       DBusMessageIter entry, variant;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                               &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "y",
> +                                               &variant);
> +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void dict_append_int32(DBusMessageIter *dict, const char *key,
> +                               dbus_int32_t val)
> +{
> +       DBusMessageIter entry, variant;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                               &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "i",
> +                                               &variant);
> +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &val);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void dict_append_uint32(DBusMessageIter *dict, const char *key,
> +                               dbus_uint32_t val)
> +{
> +       DBusMessageIter entry, variant;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                               &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "u",
> +                                               &variant);
> +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void dict_append_uint64(DBusMessageIter *dict, const char *key,
> +                               dbus_uint64_t val)
> +{
> +       DBusMessageIter entry, variant;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                               &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "t",
> +                                               &variant);
> +       dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT64, &val);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void dict_append_byte_array(DBusMessageIter *dict, const char *key,
> +                                       const uint8_t *data, int len)
> +{
> +       DBusMessageIter entry, variant, arr;
> +       int i;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                               &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> +                                               &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> +       for (i = 0; i < len; i++)
> +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &data[i]);
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void append_mode_zero(DBusMessageIter *dict,
> +                               const struct cs_mode_zero_data *m0)
> +{
> +       dict_append_byte(dict, "packetQuality", m0->packet_quality);
> +       dict_append_byte(dict, "packetRssiDbm", m0->packet_rssi_dbm);
> +       dict_append_byte(dict, "packetAntenna", m0->packet_ant);
> +       dict_append_int32(dict, "initiatorMeasuredFreqOffset",
> +                               (dbus_int32_t)m0->init_measured_freq_offset);
> +}
> +
> +static void append_pct_iq_pair(DBusMessageIter *dict, const char *key,
> +                               const struct pct_iq_sample *pct)
> +{
> +       DBusMessageIter entry, variant, arr;
> +       dbus_int32_t i_s = pct->i_sample;
> +       dbus_int32_t q_s = pct->q_sample;
> +
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                        &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> +       dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void append_mode_one(DBusMessageIter *dict,
> +                               const struct cs_mode_one_data *m1)
> +{
> +       dict_append_byte(dict, "packetQuality", m1->packet_quality);
> +       dict_append_byte(dict, "packetNadm", m1->packet_nadm);
> +       dict_append_byte(dict, "packetRssiDbm", m1->packet_rssi_dbm);
> +       dict_append_int32(dict, "toaTodInitiator",
> +                               (dbus_int32_t)m1->toa_tod_init);
> +       dict_append_int32(dict, "todToaReflector",
> +                               (dbus_int32_t)m1->tod_toa_refl);
> +       dict_append_byte(dict, "packetAntenna", m1->packet_ant);
> +       append_pct_iq_pair(dict, "packetPct1", &m1->packet_pct1);
> +       append_pct_iq_pair(dict, "packetPct2", &m1->packet_pct2);
> +}
> +
> +static void append_mode_two(DBusMessageIter *dict,
> +                               const struct cs_mode_two_data *m2,
> +                               uint8_t num_ant_paths)
> +{
> +       DBusMessageIter entry, variant, arr;
> +       const char *key;
> +       int j;
> +       int num_paths;
> +
> +       /*
> +        * num_ant_paths is the HCI "number of antenna paths" value
> +        * (0-indexed), so actual tone sample count = num_ant_paths + 1,
> +        * capped at array size.
> +        */
> +       num_paths = (num_ant_paths + 1) < CS_MAX_ANT_PATHS ?
> +                               (num_ant_paths + 1) : CS_MAX_ANT_PATHS;
> +
> +       dict_append_byte(dict, "antennaPermutationIndex", m2->ant_perm_index);
> +
> +       /* tonePctIQSamples: interleaved (i,q) pairs as "ai" */
> +       key = "tonePctIQSamples";
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                        &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ai",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "i", &arr);
> +       for (j = 0; j < num_paths; j++) {
> +               dbus_int32_t i_s = m2->tone_pct[j].i_sample;
> +               dbus_int32_t q_s = m2->tone_pct[j].q_sample;
> +
> +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &i_s);
> +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_INT32, &q_s);
> +       }
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +
> +       /* toneQualityIndicators: "ay" */
> +       key = "toneQualityIndicators";
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                        &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "y", &arr);
> +       for (j = 0; j < num_paths; j++) {
> +               uint8_t tqi = m2->tone_quality_indicator[j];
> +
> +               dbus_message_iter_append_basic(&arr, DBUS_TYPE_BYTE, &tqi);
> +       }
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void append_proc_enable_config(DBusMessageIter *outer_dict,
> +                               const struct rap_ev_cs_proc_enable_cmplt *cfg)
> +{
> +       DBusMessageIter entry, variant, inner;
> +       const char *key = "procedureEnableConfig";
> +       dbus_uint32_t sub_evt_len_us;
> +
> +       sub_evt_len_us = cfg->sub_evt_len[0] |
> +                        ((uint32_t)cfg->sub_evt_len[1] << 8) |
> +                        ((uint32_t)cfg->sub_evt_len[2] << 16);
> +
> +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> +                                        NULL, &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> +                                        &inner);
> +
> +       dict_append_byte(&inner, "toneAntennaConfigSelection",
> +                        cfg->tone_ant_config_sel);
> +       dict_append_uint32(&inner, "subeventLenUs", sub_evt_len_us);
> +       dict_append_byte(&inner, "subeventsPerEvent", cfg->sub_evts_per_evt);
> +       dict_append_uint32(&inner, "subeventInterval", cfg->sub_evt_intrvl);
> +       dict_append_uint32(&inner, "eventInterval", cfg->evt_intrvl);
> +       dict_append_uint32(&inner, "procedureInterval", cfg->proc_intrvl);
> +       dict_append_uint32(&inner, "procedureCount", cfg->proc_counter);
> +       dict_append_uint32(&inner, "maxProcedureLen", cfg->max_proc_len);
> +
> +       dbus_message_iter_close_container(&variant, &inner);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(outer_dict, &entry);
> +}
> +
> +static void append_cs_config_param(DBusMessageIter *outer_dict,
> +                               const struct bcs_procedure_data *bcs)
> +{
> +       DBusMessageIter entry, variant, inner;
> +       const char *key = "csConfigParam";
> +       const struct rap_ev_cs_config_cmplt *cfg = &bcs->cs_config;
> +
> +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> +                                        NULL, &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "{sv}",
> +                                        &inner);
> +
> +       dict_append_byte(&inner, "modeType", cfg->main_mode_type);
> +       dict_append_byte(&inner, "subModeType", cfg->sub_mode_type);
> +       dict_append_byte(&inner, "rttType", cfg->rtt_type);
> +       dict_append_byte_array(&inner, "channelMap", cfg->channel_map,
> +                              sizeof(cfg->channel_map));
> +       dict_append_byte(&inner, "minMainModeSteps", cfg->min_main_mode_steps);
> +       dict_append_byte(&inner, "maxMainModeSteps", cfg->max_main_mode_steps);
> +       dict_append_byte(&inner, "mainModeRepetition", cfg->main_mode_rep);
> +       dict_append_byte(&inner, "mode0Steps", cfg->mode_0_steps);
> +       dict_append_byte(&inner, "role", cfg->role);
> +       dict_append_byte(&inner, "csSyncPhyType", cfg->cs_sync_phy);
> +       dict_append_byte(&inner, "channelSelectionType", cfg->channel_sel_type);
> +       dict_append_byte(&inner, "ch3cShapeType", cfg->ch3c_shape);
> +       dict_append_byte(&inner, "ch3cJump", cfg->ch3c_jump);
> +       dict_append_byte(&inner, "channelMapRepetition", cfg->channel_map_rep);
> +       dict_append_byte(&inner, "tIp1TimeUs", cfg->t_ip1_time);
> +       dict_append_byte(&inner, "tIp2TimeUs", cfg->t_ip2_time);
> +       dict_append_byte(&inner, "tFcsTimeUs", cfg->t_fcs_time);
> +       dict_append_byte(&inner, "tPmTimeUs", cfg->t_pm_time);
> +       dict_append_byte(&inner, "tSwTimeUsSupportedByLocal",
> +                        bcs->t_sw_time_us_supported_by_local);
> +       dict_append_byte(&inner, "tSwTimeUsSupportedByRemote",
> +                        bcs->t_sw_time_us_supported_by_remote);
> +       dict_append_uint32(&inner, "bleConnInterval", bcs->ble_conn_interval);
> +
> +       dbus_message_iter_close_container(&variant, &inner);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(outer_dict, &entry);
> +}
> +
> +static void append_step_to_dict(DBusMessageIter *dict,
> +                               const struct cs_step_data *step,
> +                               uint8_t num_ant_paths)
> +{
> +       DBusMessageIter entry, variant, inner;
> +       const char *mode_key;
> +
> +       dict_append_byte(dict, "stepMode", step->step_mode);
> +       dict_append_byte(dict, "stepChannel", step->step_chnl);
> +
> +       switch (step->step_mode) {
> +       case CS_MODE_ZERO:
> +               mode_key = "modeZeroData";
> +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> +                                                NULL, &entry);
> +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> +                                              &mode_key);
> +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> +                                                "a{sv}", &variant);
> +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> +                                                "{sv}", &inner);
> +               append_mode_zero(&inner,
> +                                &step->step_mode_data.mode_zero_data);
> +               dbus_message_iter_close_container(&variant, &inner);
> +               dbus_message_iter_close_container(&entry, &variant);
> +               dbus_message_iter_close_container(dict, &entry);
> +               break;
> +
> +       case CS_MODE_ONE:
> +               mode_key = "modeOneData";
> +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> +                                                NULL, &entry);
> +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> +                                              &mode_key);
> +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> +                                                "a{sv}", &variant);
> +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> +                                                "{sv}", &inner);
> +               append_mode_one(&inner,
> +                               &step->step_mode_data.mode_one_data);
> +               dbus_message_iter_close_container(&variant, &inner);
> +               dbus_message_iter_close_container(&entry, &variant);
> +               dbus_message_iter_close_container(dict, &entry);
> +               break;
> +
> +       case CS_MODE_TWO:
> +               mode_key = "modeTwoData";
> +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> +                                                NULL, &entry);
> +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> +                                              &mode_key);
> +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> +                                                "a{sv}", &variant);
> +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> +                                                "{sv}", &inner);
> +               append_mode_two(&inner,
> +                               &step->step_mode_data.mode_two_data,
> +                               num_ant_paths);
> +               dbus_message_iter_close_container(&variant, &inner);
> +               dbus_message_iter_close_container(&entry, &variant);
> +               dbus_message_iter_close_container(dict, &entry);
> +               break;
> +
> +       case CS_MODE_THREE:
> +               mode_key = "modeThreeData";
> +               dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
> +                                                NULL, &entry);
> +               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
> +                                              &mode_key);
> +               dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
> +                                                "a{sv}", &variant);
> +               dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
> +                                                "{sv}", &inner);
> +               append_mode_one(&inner,
> +                       &step->step_mode_data.mode_three_data.mode_one_data);
> +               append_mode_two(&inner,
> +                       &step->step_mode_data.mode_three_data.mode_two_data,
> +                       num_ant_paths);
> +               dbus_message_iter_close_container(&variant, &inner);
> +               dbus_message_iter_close_container(&entry, &variant);
> +               dbus_message_iter_close_container(dict, &entry);
> +               break;
> +
> +       default:
> +               break;
> +       }
> +}
> +
> +static void append_subevent_to_dict(DBusMessageIter *dict,
> +                               const struct cs_subevent_result_data *sub)
> +{
> +       DBusMessageIter entry, variant, arr;
> +       const char *key;
> +       uint32_t i;
> +
> +       dict_append_int32(dict, "startAclConnEvtCounter",
> +                         (dbus_int32_t)sub->start_acl_conn_evt_counter);
> +       dict_append_int32(dict, "freqComp", (dbus_int32_t)sub->freq_comp);
> +       dict_append_byte(dict, "refPwrLvl", (uint8_t)sub->ref_pwr_lvl);
> +       dict_append_byte(dict, "numAntPaths", sub->num_ant_paths);
> +       dict_append_byte(dict, "subeventAbortReason",
> +                        sub->subevent_abort_reason);
> +       dict_append_uint64(dict, "timestampNanos", sub->timestamp_nanos);
> +       dict_append_uint32(dict, "numSteps", sub->num_steps);
> +
> +       /* stepData: av (each element is a{sv}) */
> +       key = "stepData";
> +       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
> +                                        &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> +       if (sub->step_data) {
> +               for (i = 0; i < sub->num_steps; i++) {
> +                       DBusMessageIter inner, step_dict;
> +
> +                       dbus_message_iter_open_container(&arr,
> +                                       DBUS_TYPE_VARIANT, "a{sv}", &inner);
> +                       dbus_message_iter_open_container(&inner,
> +                                       DBUS_TYPE_ARRAY, "{sv}", &step_dict);
> +                       append_step_to_dict(&step_dict, &sub->step_data[i],
> +                                           sub->num_ant_paths);
> +                       dbus_message_iter_close_container(&inner, &step_dict);
> +                       dbus_message_iter_close_container(&arr, &inner);
> +               }
> +       }
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(dict, &entry);
> +}
> +
> +static void append_subevent_array(DBusMessageIter *outer_dict,
> +                               const char *key,
> +                               const struct cs_subevent_result_data *subevents,
> +                               uint32_t count)
> +{
> +       DBusMessageIter entry, variant, arr;
> +       uint32_t i;
> +
> +       dbus_message_iter_open_container(outer_dict, DBUS_TYPE_DICT_ENTRY,
> +                                        NULL, &entry);
> +       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
> +       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "av",
> +                                        &variant);
> +       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, "v", &arr);
> +       for (i = 0; i < count; i++) {
> +               DBusMessageIter inner, sub_dict;
> +
> +               dbus_message_iter_open_container(&arr, DBUS_TYPE_VARIANT,
> +                                                "a{sv}", &inner);
> +               dbus_message_iter_open_container(&inner, DBUS_TYPE_ARRAY,
> +                                                "{sv}", &sub_dict);
> +               append_subevent_to_dict(&sub_dict, &subevents[i]);
> +               dbus_message_iter_close_container(&inner, &sub_dict);
> +               dbus_message_iter_close_container(&arr, &inner);
> +       }
> +       dbus_message_iter_close_container(&variant, &arr);
> +       dbus_message_iter_close_container(&entry, &variant);
> +       dbus_message_iter_close_container(outer_dict, &entry);
> +}
> +
> +static void rap_emit_procedure_data(struct rap_data *data,
> +                               const struct bcs_procedure_data *bcs)
> +{
> +       DBusMessage *signal;
> +       DBusMessageIter iter, dict;
> +
> +       signal = dbus_message_new_signal(device_get_path(data->device),
> +                                        CS_INTERFACE, "ProcedureData");
> +       if (!signal) {
> +               error("Failed to allocate ProcedureData signal");
> +               return;
> +       }
> +
> +       dbus_message_iter_init_append(signal, &iter);
> +       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
> +
> +       dict_append_int32(&dict, "procedureCounter",
> +                         (dbus_int32_t)bcs->procedure_counter);
> +       dict_append_int32(&dict, "procedureSequence",
> +                         (dbus_int32_t)bcs->procedure_sequence);
> +       dict_append_byte(&dict, "initiatorSelectedTxPower",
> +                        (uint8_t)bcs->initiator_selected_tx_power);
> +       dict_append_byte(&dict, "reflectorSelectedTxPower",
> +                        (uint8_t)bcs->reflector_selected_tx_power);
> +
> +       dict_append_uint32(&dict, "initiatorSubeventCount",
> +                          bcs->initiator_subevent_count);
> +       if (bcs->initiator_subevent_results &&
> +                                       bcs->initiator_subevent_count > 0)
> +               append_subevent_array(&dict, "initiatorSubeventResults",
> +                                     bcs->initiator_subevent_results,
> +                                     bcs->initiator_subevent_count);
> +
> +       dict_append_byte(&dict, "initiatorProcedureAbortReason",
> +                        bcs->initiator_procedure_abort_reason);
> +
> +       dict_append_uint32(&dict, "reflectorSubeventCount",
> +                          bcs->reflector_subevent_count);
> +       if (bcs->reflector_subevent_results &&
> +                                       bcs->reflector_subevent_count > 0)
> +               append_subevent_array(&dict, "reflectorSubeventResults",
> +                                     bcs->reflector_subevent_results,
> +                                     bcs->reflector_subevent_count);
> +
> +       dict_append_byte(&dict, "reflectorProcedureAbortReason",
> +                        bcs->reflector_procedure_abort_reason);
> +
> +       append_proc_enable_config(&dict, &bcs->proc_enable_config);
> +       append_cs_config_param(&dict, bcs);
> +
> +       dbus_message_iter_close_container(&iter, &dict);
> +
> +       g_dbus_send_message(btd_get_dbus_connection(), signal);
> +}
> +
> +static void rap_procedure_data(struct bt_rap *rap,
> +                               struct bcs_procedure_data *bcs,
> +                               void *user_data)
> +{
> +       struct rap_data *data = user_data;
> +
> +       DBG("procedure_counter=%u", bcs->procedure_counter);
> +       rap_emit_procedure_data(data, bcs);
> +}
> +
>  static DBusMessage *start_measurement(DBusConnection *conn,
>                                 DBusMessage *msg, void *user_data)
>  {
> @@ -534,6 +1020,9 @@ bad_type:
>         data->active_session.cfg           = cfg;
>         data->active_session.freq          = freq;
>
> +       bt_rap_hci_set_procedure_data_cb(data->hci_sm, rap_procedure_data,
> +                                        data, NULL);
> +
>         return dbus_message_new_method_return(msg);
>  }
>
> @@ -588,6 +1077,11 @@ static const GDBusPropertyTable cs_dbus_properties[] = {
>         { }
>  };
>
> +static const GDBusSignalTable cs_dbus_signals[] = {
> +       { GDBUS_SIGNAL("ProcedureData", GDBUS_ARGS({ "data", "a{sv}" })) },
> +       { }
> +};
> +
>  static void rap_measurement_timeout_cb(void *user_data)
>  {
>         struct rap_data *data = user_data;
> @@ -752,7 +1246,8 @@ static int rap_accept(struct btd_service *service)
>         g_dbus_register_interface(btd_get_dbus_connection(),
>                                   device_get_path(data->device),
>                                   CS_INTERFACE, cs_dbus_methods,
> -                                 NULL, cs_dbus_properties, data, NULL);
> +                                 cs_dbus_signals, cs_dbus_properties,
> +                                 data, NULL);
>
>         return 0;
>  }
> diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
> index e57f967a2..3da98972e 100644
> --- a/profiles/ranging/rap_hci.c
> +++ b/profiles/ranging/rap_hci.c
> @@ -442,6 +442,8 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
>         DBG("Sending read remote capabilities for handle 0x%04X",
>                 sm->active_conn_handle);
>         bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle);
> +
> +       bt_rap_set_local_sw_time(sm->rap, rsp->t_sw_time_supported);
>  }
>
>  static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
> @@ -873,6 +875,8 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
>                 cs_set_state(sm, CS_STATE_INIT);
>                 rap_send_hci_def_settings_command(sm, evt);
>         }
> +
> +       bt_rap_set_remote_sw_time(sm->rap, evt->t_sw_time_supported);
>  }
>
>  static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
> @@ -1162,6 +1166,46 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
>                         &rap_ev, sm->rap);
>  }
>
> +static void rap_le_conn_update_complete_evt(const void *data, uint8_t size,
> +                                           void *user_data)
> +{
> +       struct cs_state_machine *sm = user_data;
> +       const struct bt_hci_evt_le_conn_update_complete *evt;
> +       struct rap_conn_mapping *mapping;
> +       struct bt_rap *rap;
> +       struct iovec iov;
> +
> +       if (!sm || !data ||
> +           size < sizeof(struct bt_hci_evt_le_conn_update_complete))
> +               return;
> +
> +       iov.iov_base = (void *) data;
> +       iov.iov_len = size;
> +
> +       evt = util_iov_pull_mem(&iov, sizeof(*evt));
> +       if (!evt) {
> +               error("Failed to pull LE conn update complete struct");
> +               return;
> +       }
> +
> +       DBG("status=0x%02X handle=0x%04X interval=%u",
> +           evt->status, evt->handle, evt->interval);
> +
> +       if (evt->status != 0)
> +               return;
> +
> +       mapping = find_mapping_by_handle(sm, evt->handle);
> +       if (mapping && mapping->rap) {
> +               DBG("Found handle 0x%04X in mapping cache", evt->handle);
> +               rap = mapping->rap;
> +       } else {
> +               error("No mapping found for handle 0x%04X", evt->handle);
> +               return;
> +       }
> +
> +       bt_rap_set_conn_interval(rap, evt->interval);
> +}
> +
>  static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
>                                 int16_t *q_sample)
>  {
> @@ -1662,6 +1706,8 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
>                                         rap_cs_subevt_result_evt },
>                 { BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE,
>                                         rap_cs_subevt_result_cont_evt },
> +               { BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE,
> +                                       rap_le_conn_update_complete_evt },
>         };
>         struct cs_state_machine *sm;
>         unsigned int i;
> @@ -1781,6 +1827,19 @@ bool bt_rap_stop_measurement(void *hci_sm)
>                                                 false);
>  }
>
> +bool bt_rap_hci_set_procedure_data_cb(void *hci_sm,
> +                               bt_rap_procedure_data_func_t cb,
> +                               void *user_data,
> +                               bt_rap_destroy_func_t destroy)
> +{
> +       struct cs_state_machine *sm = hci_sm;
> +
> +       if (!sm || !sm->rap)
> +               return false;
> +
> +       return bt_rap_set_procedure_data_cb(sm->rap, cb, user_data, destroy);
> +}
> +
>  bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap,
>                 uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
>                 bool is_central)
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* RE: CS: Add create_context support
From: bluez.test.bot @ 2026-07-17 15:06 UTC (permalink / raw)
  To: linux-bluetooth, naga.akella
In-Reply-To: <20260717133503.3711396-2-naga.akella@oss.qualcomm.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      1.93 seconds
GitLint                       FAIL      1.31 seconds
BuildEll                      PASS      20.56 seconds
BluezMake                     PASS      539.63 seconds
MakeCheck                     PASS      18.60 seconds
MakeDistcheck                 PASS      159.70 seconds
CheckValgrind                 PASS      230.06 seconds
CheckSmatch                   PASS      312.23 seconds
bluezmakeextell               PASS      102.37 seconds
IncrementalBuild              PASS      557.25 seconds
ScanBuild                     PASS      974.91 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v1,2/4] profiles: Add create_context param as part of CS Create Config cmd

1: T1 Title exceeds max length (81>80): "[BlueZ,v1,2/4] profiles: Add create_context param as part of CS Create Config cmd"


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH v4 03/11] power: sequencing: Add pwrseq_is_controllable() API
From: Loic Poulain @ 2026-07-17 14:45 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-pci, linux-pm, linux-kernel, linux-arm-msm, linux-bluetooth,
	devicetree, Manivannan Sadhasivam, Manivannan Sadhasivam,
	Marcel Holtmann, Luiz Augusto von Dentz, Bjorn Andersson,
	Konrad Dybcio, Rob Herring, Krzysztof Kozlowski, Conor Dooley
In-Reply-To: <CAMRc=Md8mLgYRsLd-KhwVV8X9AcYbHf--RB_pf0xFBP_P_DvCQ@mail.gmail.com>

Hi Bartosz,

On Fri, Jul 17, 2026 at 11:29 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>
> On Thu, 16 Jul 2026 18:18:20 +0200, Loic Poulain
> <loic.poulain@oss.qualcomm.com> said:
> > On some boards a power sequencing target has no host-controllable enable
> > for its function, for instance when the enable line is not wired up to a
> > GPIO and is hardwired to an always-on level. The pcie-m2 "uart" target is
> > one such example: when the M.2 connector does not route the W_DISABLE2#
> > signal to a host GPIO, its enable/disable are no-ops and the consumer
> > cannot gate the Bluetooth function at all or exclusively.
> >
> > Add a generic pwrseq_is_controllable() helper. It reports whether the
> > target's final unit provides a host-controllable dedicated power actuator.
> > The target can implement a new optional is_controllable() callback,
> > reporting whether that actuator is effective on this target (for example
> > depending on GPIO presence). If the target does not provide the callback,
> > it is assumed to be controllable.
> >
> > Note this only describes the target's own enable actuator. It does not
> > imply that a power-off reaches an electrical OFF state, since a target may
> > have multiple consumers. It also does not mean that power is uncontrolled
> > for the target's dependencies: those may still be gated on their own. And
> > it does not restrict consumers from calling pwrseq_power_off() either,
> > which remains valid to drop this consumer's vote on the (possibly shared)
> > resources and dependencies of the target.
> >
> > Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > ---
>
> Thanks, this looks good to me now. This series is a bit all over the place,
> what is the merge strategy?

Since the entire series ultimately depends/touch on the pwrseq topic,
the simplest approach may be to take it all through your pwrseq
branch, with the possible exception of the DTS patches (through
Bjorn)?

Regards,
Loic

^ permalink raw reply

* [bluez/bluez] 7059a2: shared: Add create_context CS config parameter
From: Bhavani @ 2026-07-17 14:17 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1129618
  Home:   https://github.com/bluez/bluez
  Commit: 7059a2547f09eb71e3fe0faf597918d9e658ed73
      https://github.com/bluez/bluez/commit/7059a2547f09eb71e3fe0faf597918d9e658ed73
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-07-17 (Fri, 17 Jul 2026)

  Changed paths:
    M src/shared/cs-types.h

  Log Message:
  -----------
  shared: Add create_context CS config parameter

Add create_context in bt_rap_le_cs_config structure


  Commit: ee37dfde9ccfd98fd8ac9d19065d10b654c6db0b
      https://github.com/bluez/bluez/commit/ee37dfde9ccfd98fd8ac9d19065d10b654c6db0b
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-07-17 (Fri, 17 Jul 2026)

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

  Log Message:
  -----------
  profiles: Add create_context param as part of CS Create Config cmd

- Add the create_context field to the CS Create Config command.
- Route HCI command-send failures and non-zero-status completion
events to CS_STATE_HOLD instead of CS_STATE_STOPPED.
reserving CS_STATE_STOPPED for the clean/expected
stop (Procedure Enable Complete with state == 0x00)


  Commit: 54de92d0bf259b924f89566ab777043fa467d900
      https://github.com/bluez/bluez/commit/54de92d0bf259b924f89566ab777043fa467d900
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-07-17 (Fri, 17 Jul 2026)

  Changed paths:
    M client/cs.c
    M client/cs.h

  Log Message:
  -----------
  client: Add create_context CS config parameter

- Add the create_context field to the CS config parameter table so
bluetoothctl can control whether cs.start writes the CS configuration
to the local Controller only (0x00) or to both local and remote
Controllers via the CS Configuration procedure (0x01), matching the
Create_Context field of the LE CS Create Config HCI command
- Change the license in cs.c and cs.h.
- Drop RangingInterface support for now,
as it is still under discussion.


  Commit: 9efbc3b2c56a230045209606316e6bcb3dee6f5e
      https://github.com/bluez/bluez/commit/9efbc3b2c56a230045209606316e6bcb3dee6f5e
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-07-17 (Fri, 17 Jul 2026)

  Changed paths:
    M doc/bluetoothctl-cs.rst

  Log Message:
  -----------
  doc: Document create_context in bluetoothctl-cs.rst

Document the new create_context parameter introduced in client/cs.c.


Compare: https://github.com/bluez/bluez/compare/7059a2547f09%5E...9efbc3b2c56a

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

^ permalink raw reply


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