Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v15 6/7] Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
From: Chris Lu @ 2026-07-17  7:42 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717074207.2869999-1-chris.lu@mediatek.com>

Add BTMTK_WMT_PKT_* enum to represent WMT download packet sequence flags,
improving code readability. Replace magic numbers (1, 2, 3) in
btmtk_setup_firmware_79xx() and btmtk_setup_firmware() with descriptive
enum values:

- BTMTK_WMT_PKT_START (1): First packet of a sequence
- BTMTK_WMT_PKT_CONTINUE (2): Continuation packet
- BTMTK_WMT_PKT_END (3): Final packet of a sequence

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtk.c | 12 ++++++------
 drivers/bluetooth/btmtk.h |  6 ++++++
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 0fd6ce09a313..e059e72d9dbe 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -271,12 +271,12 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 			while (dl_size > 0) {
 				dlen = min_t(int, 250, dl_size);
 				if (first_block == 1) {
-					flag = 1;
+					flag = BTMTK_WMT_PKT_START;
 					first_block = 0;
 				} else if (dl_size - dlen <= 0) {
-					flag = 3;
+					flag = BTMTK_WMT_PKT_END;
 				} else {
-					flag = 2;
+					flag = BTMTK_WMT_PKT_CONTINUE;
 				}
 
 				wmt_params.flag = flag;
@@ -355,7 +355,7 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
 
 	fw_size -= 30;
 	fw_ptr += 30;
-	flag = 1;
+	flag = BTMTK_WMT_PKT_START;
 
 	wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
 	wmt_params.status = NULL;
@@ -365,9 +365,9 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
 
 		/* Tell device the position in sequence */
 		if (fw_size - dlen <= 0)
-			flag = 3;
+			flag = BTMTK_WMT_PKT_END;
 		else if (fw_size < fw->size - 30)
-			flag = 2;
+			flag = BTMTK_WMT_PKT_CONTINUE;
 
 		wmt_params.flag = flag;
 		wmt_params.dlen = dlen;
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..c234e5ea27e5 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -66,6 +66,12 @@ enum {
 	BTMTK_WMT_ON_PROGRESS,
 };
 
+enum btmtk_wmt_pkt_flag {
+	BTMTK_WMT_PKT_START = 1,
+	BTMTK_WMT_PKT_CONTINUE = 2,
+	BTMTK_WMT_PKT_END = 3,
+};
+
 struct btmtk_wmt_hdr {
 	u8	dir;
 	u8	op;
-- 
2.45.2


^ permalink raw reply related

* [PATCH v15 0/7] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-07-17  7:41 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu

This patch series adds support for MT7928 (device ID 0x7935) to the
btmtk driver, which requires a new two-stage firmware loading process
with CBMCU firmware.

Patch 1 fixes SKB handling issues: DMA out-of-bounds read and cloned
SKB corruption. Fix by calling skb_unshare() and expanding skb tailroom
with zero-padding.

Patch 2 adds firmware size validation with integer overflow protection
to prevent out-of-bounds access with malicious firmware files.

Patch 3 passes hardware dev_id to mt79xx_setup() to fix incorrect chip
ID logging (was hardcoded to 0).

Patch 4 removes redundant snprintf() that overrides btmtk_fw_get_filename()
result, causing newer chips to request incorrect firmware filenames.

Patch 5 improves firmware logging: adds filename log, uses chip ID as
HW version, and uses %.16s format specifier for datetime field.

Patch 6 replaces magic numbers with BTMTK_WMT_PKT_* enum for better
code readability.

Patch 7 implements MT7928 CBMCU firmware download with two-phase loading
sequence and cert_len overflow protection.

Tested on MT7928 hardware with successful firmware loading and
Bluetooth functionality verification.

Changes in v15:
- Rebased onto latest bluetooth-next/HEAD (bd8bee79e1fa)
- No functional changes from v14
- Fixed patch application issues reported by Sashiko

Changes in v14:
- Rebased onto latest bluetooth-next tree
- No functional changes from v13

Changes in v13:
- Fix cloned SKB corruption (HIGH severity) by adding skb_unshare() (Patch 1)
- Fix cert_len integer overflow (CRITICAL severity) in CBMCU firmware loading (Patch 7)

Pre-existing issues fixed in subsequent patches:
- Patch 2 introduces format string issue (%s on non-null-terminated field)
  -> Fixed in Patch 5 using %.16s format specifier
- Patch 3 context has pre-existing snprintf() override (identified in v12 review)
  -> Fixed in Patch 4, resolving firmware load failures on MT6639/MT7925/MT7928
This gradual improvement approach allows independent backporting of each fix.

v12 -> v13:
- Added skb_unshare() to prevent cloned SKB corruption
- Added overflow checks for cert_len calculation
- Split SDIO firmware loading fixes into two patches

Changes in v12:
- Reordered patches to fix dependency: move "Pass dev_id" before
  "Improve logging" so dev_id is available when logging

Changes in v11:
- Fixed use-after-free: reassign sdio_hdr after pskb_expand_head()
- Reordered patches for bisectability

Changes in v10:
- Added Patch 1 to fix DMA out-of-bounds access

Changes in v9:
- Reordered patches to group SDIO fixes together

Changes in v8:
- Split firmware validation from dev_id pass-through into separate patches

Changes in v7:
- Added Fixes tag per feedback

Changes in v6:
- Added Fixes tag and restructured parameter comment

Changes in v5:
- Moved documentation to function-level comment

Changes in v4:
- Fixed commit message and expanded description

Changes in v3:
- Changed dev_id type from u16 to u32

Changes in v2:
- Combined MT7928 functionality into single patch

Chris Lu (7):
  Bluetooth: btmtksdio: Fix SKB handling issues in TX path
  Bluetooth: btmtk: Add firmware size validation in
    btmtk_setup_firmware_79xx()
  Bluetooth: btmtksdio: Pass hardware dev_id to mt79xx_setup()
  Bluetooth: btmtksdio: Remove redundant firmware filename override
  Bluetooth: btmtk: Improve BT firmware logging
  Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
  Bluetooth: btmtk: Add MT7928 support

 drivers/bluetooth/btmtk.c     | 427 +++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btmtk.h     |   9 +
 drivers/bluetooth/btmtksdio.c |  55 ++++-
 3 files changed, 471 insertions(+), 20 deletions(-)

-- 
2.45.2


^ permalink raw reply

* [PATCH BlueZ v1 2/2] profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding
From: Prathibha Madugonde @ 2026-07-17  7:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260717072647.255002-1-prathm@qti.qualcomm.com>

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.

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


^ permalink raw reply related

* [PATCH BlueZ v1 1/2] shared/rap: Add bcs_procedure_data aggregation and procedure data API
From: Prathibha Madugonde @ 2026-07-17  7:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260717072647.255002-1-prathm@qti.qualcomm.com>

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

Define bcs_procedure_data to hold per-procedure CS results from both
the local initiator and remote reflector, including subevent step data,
selected TX powers, procedure enable config, CS config, sw_time values,
and BLE connection interval.

Add cs_proc_state with a 16-entry ring buffer in cstracker to track
procedures keyed by procedure counter, accumulating results
independently from each side until both report all-results-complete.

Update parse_mode_{0,1,2,3}, parse_step, and parse_subevent_steps with
output pointer parameters to capture remote reflector step data.
Introduce parse_cs_local_initiator_data() to store local HCI subevent
results, handling both initial and continuation events. Update
parse_ras_data_segments() to route decoded RAS subevent data into the
matching cs_proc_state as reflector results.

Add bt_rap_set_procedure_data_cb() to register a callback fired once
both local and remote results are complete for a given procedure.
Add bt_rap_set_local_sw_time(), bt_rap_set_remote_sw_time(), and
bt_rap_set_conn_interval() so callers such as rap_hci.c can supply the
CS capability sw_time and connection interval required by upper-layer
distance calculation algorithms.

Remove unwanted function rap_detached , fix for rap profile level
disconnections

---
 src/shared/rap.c | 673 ++++++++++++++++++++++++++++++++++++++++++++---
 src/shared/rap.h |  50 ++++
 2 files changed, 687 insertions(+), 36 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index dfb272d3a..eadc41707 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -12,6 +12,8 @@
 #include <stdbool.h>
 #include <unistd.h>
 #include <errno.h>
+#include <time.h>
+#include <glib.h>
 
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/hci.h"
@@ -151,6 +153,16 @@ static inline uint8_t ranging_header_get_antenna_mask(
 	return hdr->antenna_pct & 0x0F;
 }
 
+static inline uint16_t ranging_header_get_counter(
+					const struct ranging_header *hdr)
+{
+	if (!hdr)
+		return 0;
+
+	return (uint16_t)(hdr->counter_config[0] |
+			  ((hdr->counter_config[1] & 0x0F) << 8));
+}
+
 static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
 {
 	uint8_t count = 0;
@@ -206,11 +218,23 @@ enum cs_role {
 };
 
 #define CS_INVALID_CONFIG_ID   0xFF
+#define CS_RANGING_COUNTER_MASK   0x0FFF
+
 /* Minimal enums (align to controller values if needed) */
 enum cs_procedure_done_status {
 	CS_PROC_ALL_RESULTS_COMPLETE = 0x00,
 	CS_PROC_PARTIAL_RESULTS      = 0x01,
-	CS_PROC_ABORTED              = 0x02
+	CS_PROC_ABORTED              = 0x0F
+};
+
+/* Per-procedure tracking — one entry per HCI procedure counter */
+struct cs_proc_state {
+	uint16_t proc_counter;
+	struct bcs_procedure_data bcs_data;
+	enum cs_procedure_done_status local_status;
+	bool                          local_has_complete_subevent;
+	enum cs_procedure_done_status remote_status;
+	bool                          remote_has_complete_subevent;
 };
 
 /* Main cs_procedure_data  */
@@ -253,6 +277,12 @@ struct cstracker {
 	struct ranging_header   ranging_header_;
 	/* Client - subsequent segments appended using iovec */
 	struct iovec segment_data;
+
+	/* Session-level config template and per-procedure state */
+	struct bcs_procedure_data     bcs_proc_data;
+	struct queue                  *proc_states;
+	uint8_t                       procedure_sequence_after_enable;
+	uint64_t                      proc_start_timestamp_nanos;
 };
 
 /* Ranging Service context */
@@ -300,6 +330,15 @@ struct bt_rap {
 	bt_rap_destroy_func_t debug_destroy;
 	void *debug_data;
 	void *user_data;
+
+	bt_rap_procedure_data_func_t  procedure_data_cb;
+	bt_rap_destroy_func_t         procedure_data_destroy;
+	void                         *procedure_data_user_data;
+
+	/* CS capabilities sw_time and connection interval for config param */
+	uint8_t local_sw_time;
+	uint8_t remote_sw_time;
+	uint16_t conn_interval;
 	struct cstracker *resptracker;
 	struct cstracker *reqtracker;
 };
@@ -542,6 +581,177 @@ static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
 	return true;
 }
 
+/* ---------- bcs_procedure_data helpers ----------------------------------- */
+static uint64_t get_time_nanos(void)
+{
+	struct timespec ts;
+
+	clock_gettime(CLOCK_REALTIME, &ts);
+	return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+static void bcs_proc_data_clear(struct bcs_procedure_data *proc)
+{
+	uint32_t i;
+
+	if (!proc)
+		return;
+
+	for (i = 0; i < proc->initiator_subevent_count; i++)
+		free(proc->initiator_subevent_results[i].step_data);
+	free(proc->initiator_subevent_results);
+	proc->initiator_subevent_results = NULL;
+	proc->initiator_subevent_count = 0;
+
+	for (i = 0; i < proc->reflector_subevent_count; i++)
+		free(proc->reflector_subevent_results[i].step_data);
+	free(proc->reflector_subevent_results);
+	proc->reflector_subevent_results = NULL;
+	proc->reflector_subevent_count = 0;
+}
+
+static bool bcs_proc_data_add_initiator_subevent(
+					struct bcs_procedure_data *proc,
+					uint16_t start_acl_evt,
+					uint16_t freq_comp,
+					int8_t ref_pwr,
+					uint8_t num_ant_paths,
+					uint8_t abort_reason,
+					uint64_t timestamp_nanos,
+					struct cs_step_data *steps,
+					uint32_t num_steps)
+{
+	struct cs_subevent_result_data *arr;
+	uint32_t new_count;
+
+	if (!proc)
+		return false;
+
+	new_count = proc->initiator_subevent_count + 1;
+	arr = realloc(proc->initiator_subevent_results,
+					new_count * sizeof(*arr));
+	if (!arr) {
+		free(steps);
+		return false;
+	}
+
+	arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
+	arr[new_count - 1].freq_comp = freq_comp;
+	arr[new_count - 1].ref_pwr_lvl = ref_pwr;
+	arr[new_count - 1].num_ant_paths = num_ant_paths;
+	arr[new_count - 1].subevent_abort_reason = abort_reason;
+	arr[new_count - 1].timestamp_nanos = timestamp_nanos;
+	arr[new_count - 1].num_steps = num_steps;
+	arr[new_count - 1].step_data = steps;
+
+	proc->initiator_subevent_results = arr;
+	proc->initiator_subevent_count = new_count;
+	return true;
+}
+
+static bool bcs_proc_data_add_reflector_subevent(
+					struct bcs_procedure_data *proc,
+					uint16_t start_acl_evt,
+					uint16_t freq_comp,
+					int8_t ref_pwr,
+					uint8_t num_ant_paths,
+					uint8_t abort_reason,
+					uint64_t timestamp_nanos,
+					struct cs_step_data *steps,
+					uint32_t num_steps)
+{
+	struct cs_subevent_result_data *arr;
+	uint32_t new_count;
+
+	if (!proc)
+		return false;
+
+	new_count = proc->reflector_subevent_count + 1;
+	arr = realloc(proc->reflector_subevent_results,
+					new_count * sizeof(*arr));
+	if (!arr) {
+		free(steps);
+		return false;
+	}
+
+	arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
+	arr[new_count - 1].freq_comp = freq_comp;
+	arr[new_count - 1].ref_pwr_lvl = ref_pwr;
+	arr[new_count - 1].num_ant_paths = num_ant_paths;
+	arr[new_count - 1].subevent_abort_reason = abort_reason;
+	arr[new_count - 1].timestamp_nanos = timestamp_nanos;
+	arr[new_count - 1].num_steps = num_steps;
+	arr[new_count - 1].step_data = steps;
+
+	proc->reflector_subevent_results = arr;
+	proc->reflector_subevent_count = new_count;
+	return true;
+}
+
+static bool match_proc_counter(const void *data, const void *match_data)
+{
+	const struct cs_proc_state *s = data;
+	uint16_t proc_counter = PTR_TO_UINT(match_data);
+
+	return s->proc_counter == proc_counter;
+}
+
+static void free_proc_state(void *data)
+{
+	struct cs_proc_state *s = data;
+
+	bcs_proc_data_clear(&s->bcs_data);
+	free(s);
+}
+
+static struct cs_proc_state *find_or_create_proc_state(struct cstracker *t,
+							uint16_t proc_counter)
+{
+	struct cs_proc_state *s;
+
+	s = queue_find(t->proc_states, match_proc_counter,
+					UINT_TO_PTR(proc_counter));
+	if (s)
+		return s;
+
+	s = new0(struct cs_proc_state, 1);
+	s->proc_counter  = proc_counter;
+	s->local_status  = CS_PROC_PARTIAL_RESULTS;
+	s->remote_status = CS_PROC_PARTIAL_RESULTS;
+	/* Copy session-level config from template */
+	s->bcs_data.proc_enable_config = t->bcs_proc_data.proc_enable_config;
+	s->bcs_data.cs_config = t->bcs_proc_data.cs_config;
+	s->bcs_data.t_sw_time_us_supported_by_local =
+			t->bcs_proc_data.t_sw_time_us_supported_by_local;
+	s->bcs_data.t_sw_time_us_supported_by_remote =
+			t->bcs_proc_data.t_sw_time_us_supported_by_remote;
+	s->bcs_data.ble_conn_interval = t->bcs_proc_data.ble_conn_interval;
+	s->bcs_data.initiator_selected_tx_power =
+			t->bcs_proc_data.initiator_selected_tx_power;
+	s->bcs_data.procedure_counter = proc_counter;
+
+	queue_push_tail(t->proc_states, s);
+
+	return s;
+}
+
+static struct cs_proc_state *find_proc_state_for_ras(struct cstracker *t,
+						uint16_t ranging_counter)
+{
+	const struct queue_entry *entry;
+
+	for (entry = queue_get_entries(t->proc_states); entry;
+							entry = entry->next) {
+		struct cs_proc_state *s = entry->data;
+
+		if ((s->proc_counter & CS_RANGING_COUNTER_MASK) ==
+		    ranging_counter)
+			return s;
+	}
+	return NULL;
+}
+/* ---------- end bcs helpers ---------------------------------------------- */
+
 static struct ras *rap_get_ras(struct bt_rap *rap)
 {
 	if (!rap)
@@ -556,14 +766,6 @@ static struct ras *rap_get_ras(struct bt_rap *rap)
 	return rap->rrapdb->ras;
 }
 
-static void rap_detached(void *data, void *user_data)
-{
-	struct bt_rap_cb *cb = data;
-	struct bt_rap *rap = user_data;
-
-	cb->detached(rap, cb->user_data);
-}
-
 void bt_rap_detach(struct bt_rap *rap)
 {
 	if (!queue_remove(sessions, rap))
@@ -572,8 +774,6 @@ void bt_rap_detach(struct bt_rap *rap)
 	bt_gatt_client_idle_unregister(rap->client, rap->idle_id);
 	bt_gatt_client_unref(rap->client);
 	rap->client = NULL;
-
-	queue_foreach(bt_rap_cbs, rap_detached, rap);
 }
 
 static void rap_db_free(void *data)
@@ -608,15 +808,21 @@ static void rap_free(void *data)
 	rap_db_free(rap->rrapdb);
 
 	if (rap->resptracker) {
+		free(rap->resptracker->segment_data.iov_base);
 		free(rap->resptracker);
 		rap->resptracker = NULL;
 	}
 
 	if (rap->reqtracker) {
+		queue_destroy(rap->reqtracker->proc_states, free_proc_state);
+		free(rap->reqtracker->segment_data.iov_base);
 		free(rap->reqtracker);
 		rap->reqtracker = NULL;
 	}
 
+	if (rap->procedure_data_destroy)
+		rap->procedure_data_destroy(rap->procedure_data_user_data);
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -702,6 +908,24 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 	return true;
 }
 
+bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
+				  bt_rap_procedure_data_func_t cb,
+				  void *user_data,
+				  bt_rap_destroy_func_t destroy)
+{
+	if (!rap)
+		return false;
+
+	if (rap->procedure_data_destroy)
+		rap->procedure_data_destroy(rap->procedure_data_user_data);
+
+	rap->procedure_data_cb      = cb;
+	rap->procedure_data_destroy = destroy;
+	rap->procedure_data_user_data = user_data;
+
+	return true;
+}
+
 static void cs_tracker_init(struct cstracker *t)
 {
 	if (!t)
@@ -716,6 +940,7 @@ static void cs_tracker_init(struct cstracker *t)
 	t->last_start_acl_conn_evt_counter = 0;
 	t->last_freq_comp = 0;
 	t->last_ref_pwr_lvl = 0;
+	t->proc_states = queue_new();
 
 	/* Initialize ranging header using helper functions */
 	memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
@@ -1804,12 +2029,180 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
 		cont->step_data);
 }
 
+static void write_procedure_data_to_bcs_algo(struct bt_rap *rap,
+					     struct bcs_procedure_data *bcs)
+{
+	if (!rap || !bcs)
+		return;
+
+	if (rap->reqtracker && rap->reqtracker->proc_start_timestamp_nanos) {
+		uint64_t elapsed_nanos = get_time_nanos() -
+				rap->reqtracker->proc_start_timestamp_nanos;
+		uint32_t k;
+
+		DBG(rap, "Procedure elapsed time: %llu nanos",
+		    (unsigned long long)elapsed_nanos);
+
+		for (k = 0; k < bcs->initiator_subevent_count; k++)
+			bcs->initiator_subevent_results[k].timestamp_nanos =
+								elapsed_nanos;
+		for (k = 0; k < bcs->reflector_subevent_count; k++)
+			bcs->reflector_subevent_results[k].timestamp_nanos =
+								elapsed_nanos;
+	}
+
+	DBG(rap, "procedure_counter=%u sequence=%u "
+	    "init_tx_pwr=%d refl_tx_pwr=%d "
+	    "init_abort=%d refl_abort=%d "
+	    "init_subevents=%u refl_subevents=%u",
+	    bcs->procedure_counter, bcs->procedure_sequence,
+	    bcs->initiator_selected_tx_power,
+	    bcs->reflector_selected_tx_power,
+	    bcs->initiator_procedure_abort_reason,
+	    bcs->reflector_procedure_abort_reason,
+	    bcs->initiator_subevent_count,
+	    bcs->reflector_subevent_count);
+
+	if (rap->procedure_data_cb)
+		rap->procedure_data_cb(rap, bcs,
+				       rap->procedure_data_user_data);
+
+	bcs_proc_data_clear(bcs);
+}
+
+static void check_cs_procedure_complete(struct bt_rap *rap,
+					 struct cstracker *reqtracker,
+					 struct cs_proc_state *state)
+{
+	struct bcs_procedure_data *bcs = &state->bcs_data;
+
+	if (!rap->procedure_data_cb)
+		return;
+
+	if (state->local_status != CS_PROC_ALL_RESULTS_COMPLETE ||
+	    state->remote_status != CS_PROC_ALL_RESULTS_COMPLETE) {
+		DBG(rap, "Procedure not complete: local=%d remote=%d",
+		    state->local_status, state->remote_status);
+		return;
+	}
+
+	if (!state->local_has_complete_subevent &&
+	    !state->remote_has_complete_subevent) {
+		DBG(rap, "No complete subevent available");
+		return;
+	}
+
+	reqtracker->procedure_sequence_after_enable++;
+	bcs->procedure_sequence = reqtracker->procedure_sequence_after_enable;
+	write_procedure_data_to_bcs_algo(rap, bcs);
+
+	queue_remove(reqtracker->proc_states, state);
+	free(state);
+}
+
+static void parse_cs_local_initiator_data(struct bt_rap *rap,
+					bool has_header_fields,
+					uint8_t config_id,
+					uint8_t num_ant_paths,
+					uint16_t proc_counter,
+					uint16_t start_acl_conn_evt_counter,
+					uint16_t freq_comp,
+					int8_t ref_pwr_lvl,
+					uint8_t proc_done_status,
+					uint8_t subevt_done_status,
+					uint8_t abort_reason,
+					uint8_t num_steps_reported,
+					const struct cs_step_data *hci_steps)
+{
+	struct cstracker *reqtracker = rap->reqtracker;
+	uint16_t effective_counter;
+	struct cs_proc_state *state;
+	struct bcs_procedure_data *bcs;
+
+	effective_counter = has_header_fields ? proc_counter
+					      : reqtracker->last_proc_counter;
+
+	state = find_or_create_proc_state(reqtracker, effective_counter);
+	if (!state)
+		return;
+
+	bcs = &state->bcs_data;
+
+	bcs->initiator_selected_tx_power      = reqtracker->selected_tx_power;
+	bcs->initiator_procedure_abort_reason = abort_reason & 0x0F;
+
+	state->local_status =
+		(enum cs_procedure_done_status)(proc_done_status & 0x0F);
+	if ((subevt_done_status & 0x0F) == 0x00)
+		state->local_has_complete_subevent = true;
+
+	if (has_header_fields) {
+		struct cs_step_data *steps = NULL;
+
+		reqtracker->proc_start_timestamp_nanos = get_time_nanos();
+
+		bcs->procedure_counter        = proc_counter;
+		reqtracker->last_proc_counter = proc_counter;
+		reqtracker->last_start_acl_conn_evt_counter =
+					start_acl_conn_evt_counter;
+		reqtracker->last_freq_comp   = freq_comp;
+		reqtracker->last_ref_pwr_lvl = ref_pwr_lvl;
+
+		if (num_steps_reported > 0 && hci_steps) {
+			steps = calloc(num_steps_reported, sizeof(*steps));
+			if (!steps)
+				return;
+			memcpy(steps, hci_steps,
+			       num_steps_reported * sizeof(*steps));
+		}
+
+		bcs_proc_data_add_initiator_subevent(bcs,
+				start_acl_conn_evt_counter,
+				freq_comp, ref_pwr_lvl,
+				num_ant_paths,
+				(abort_reason >> 4) & 0x0F,
+				0,
+				steps, num_steps_reported);
+	} else {
+		struct cs_subevent_result_data *last;
+		struct cs_step_data *new_steps;
+		uint32_t old_count, new_count;
+
+		if (!bcs->initiator_subevent_results ||
+		    bcs->initiator_subevent_count == 0)
+			return;
+
+		last = &bcs->initiator_subevent_results[
+					bcs->initiator_subevent_count - 1];
+		old_count = last->num_steps;
+		new_count = old_count + num_steps_reported;
+
+		if (num_steps_reported > 0 && hci_steps) {
+			new_steps = realloc(last->step_data,
+					    new_count * sizeof(*new_steps));
+			if (!new_steps)
+				return;
+
+			memcpy(&new_steps[old_count], hci_steps,
+			       num_steps_reported * sizeof(*new_steps));
+			last->step_data = new_steps;
+			last->num_steps = new_count;
+		}
+
+		last->subevent_abort_reason = (abort_reason >> 4) & 0x0F;
+	}
+
+	check_cs_procedure_complete(rap, reqtracker, state);
+}
+
+
 static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
 		const struct rap_ev_cs_subevent_result_cont *cont,
 		uint16_t length)
 {
 	size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
 					step_data);
+	struct cstracker *reqtracker;
 
 	if (!rap || !rap->reqtracker || !cont)
 		return;
@@ -1819,6 +2212,22 @@ static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
 
 	DBG(rap, "Received CS subevent result continue subevent: len=%u",
 		length);
+
+	reqtracker = rap->reqtracker;
+
+	parse_cs_local_initiator_data(rap,
+				false,
+				cont->config_id,
+				cont->num_ant_paths,
+				reqtracker->last_proc_counter,
+				reqtracker->last_start_acl_conn_evt_counter,
+				reqtracker->last_freq_comp,
+				reqtracker->last_ref_pwr_lvl,
+				cont->proc_done_status,
+				cont->subevt_done_status,
+				cont->abort_reason,
+				cont->num_steps_reported,
+				cont->step_data);
 }
 
 static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
@@ -1836,7 +2245,20 @@ static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
 		return;
 
 	DBG(rap, "Received CS subevent result subevent: len=%u", length);
-	/* TODO: Store initiator subevent result data */
+
+	parse_cs_local_initiator_data(rap,
+				true,
+				data->config_id,
+				data->num_ant_paths,
+				data->proc_counter,
+				data->start_acl_conn_evt_counter,
+				data->freq_comp,
+				data->ref_pwr_lvl,
+				data->proc_done_status,
+				data->subevt_done_status,
+				data->abort_reason,
+				data->num_steps_reported,
+				data->step_data);
 }
 
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
@@ -1880,6 +2302,7 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 	const struct rap_ev_cs_proc_enable_cmplt *data = param;
 	struct bt_rap *rap = user_data;
 	struct cstracker *resptracker;
+	struct cstracker *reqtracker;
 
 	DBG(rap, "Received CS procedure enable complete subevent: len=%u",
 	    length);
@@ -1895,6 +2318,21 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 	/* Populate responder tracker */
 	resptracker->config_id = data->config_id;
 	resptracker->selected_tx_power = data->sel_tx_pwr;
+
+	if (!rap->reqtracker) {
+		reqtracker = new0(struct cstracker, 1);
+		cs_tracker_init(reqtracker);
+		rap->reqtracker = reqtracker;
+	}
+
+	reqtracker = rap->reqtracker;
+	reqtracker->config_id = data->config_id;
+	reqtracker->selected_tx_power = data->sel_tx_pwr;
+	reqtracker->bcs_proc_data.initiator_selected_tx_power =
+							data->sel_tx_pwr;
+
+	/* Store procedure enable config in the session-level template */
+	reqtracker->bcs_proc_data.proc_enable_config = *data;
 }
 
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
@@ -1945,6 +2383,39 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 	reqtracker->config_id = data->config_id;
 	reqtracker->role = data->role;
 	reqtracker->rtt_type = data->rtt_type;
+	reqtracker->procedure_sequence_after_enable = 0;
+
+	/* Store cs_config in session-level template for procedure data */
+	reqtracker->bcs_proc_data.cs_config = *data;
+	reqtracker->bcs_proc_data.t_sw_time_us_supported_by_local =
+						rap->local_sw_time;
+	reqtracker->bcs_proc_data.t_sw_time_us_supported_by_remote =
+						rap->remote_sw_time;
+	reqtracker->bcs_proc_data.ble_conn_interval = rap->conn_interval;
+}
+
+void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time)
+{
+	if (!rap)
+		return;
+
+	rap->local_sw_time = local_sw_time;
+}
+
+void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time)
+{
+	if (!rap)
+		return;
+
+	rap->remote_sw_time = remote_sw_time;
+}
+
+void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval)
+{
+	if (!rap)
+		return;
+
+	rap->conn_interval = conn_interval;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -2122,7 +2593,8 @@ static size_t get_mode_zero_length(enum cs_role remote_role)
 }
 
 static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
-			    enum cs_role remote_role)
+			    enum cs_role remote_role,
+			    struct cs_mode_zero_data *out)
 {
 	uint8_t packet_quality;
 	int8_t packet_rssi_dbm;
@@ -2143,7 +2615,12 @@ static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
 		}
 	}
 
-	/* TODO: Store this data as reflector data */
+	if (out) {
+		out->packet_quality = packet_quality;
+		out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
+		out->packet_ant = packet_ant;
+		out->init_measured_freq_offset = init_measured_freq_offset;
+	}
 }
 
 static size_t get_mode_one_length(bool include_pct)
@@ -2154,7 +2631,8 @@ static size_t get_mode_one_length(bool include_pct)
 }
 
 static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
-			   enum cs_role remote_role, bool include_pct)
+			   enum cs_role remote_role, bool include_pct,
+			   struct cs_mode_one_data *out)
 {
 	uint8_t packet_quality;
 	uint8_t packet_nadm;
@@ -2178,7 +2656,20 @@ static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
 		parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
 	}
 
-	/* TODO: Store this data as reflector data */
+	if (out) {
+		out->packet_quality = packet_quality;
+		out->packet_nadm = packet_nadm;
+		out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
+		out->packet_ant = packet_ant;
+		if (remote_role == CS_ROLE_REFLECTOR)
+			out->tod_toa_refl = time_value;
+		else
+			out->toa_tod_init = time_value;
+		out->packet_pct1.i_sample = pct1_i;
+		out->packet_pct1.q_sample = pct1_q;
+		out->packet_pct2.i_sample = pct2_i;
+		out->packet_pct2.q_sample = pct2_q;
+	}
 }
 
 static size_t get_mode_two_length(uint8_t num_antenna_paths)
@@ -2189,7 +2680,8 @@ static size_t get_mode_two_length(uint8_t num_antenna_paths)
 }
 
 static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
-			   uint8_t num_antenna_paths)
+			   uint8_t num_antenna_paths,
+			   struct cs_mode_two_data *out)
 {
 	uint8_t ant_perm_index;
 	int16_t tone_pct_i[5];
@@ -2228,7 +2720,14 @@ static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
 	DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
 		ant_perm_index);
 
-	/* TODO: Store this data as reflector data */
+	if (out) {
+		out->ant_perm_index = ant_perm_index;
+		for (k = 0; k < num_paths; k++) {
+			out->tone_pct[k].i_sample = tone_pct_i[k];
+			out->tone_pct[k].q_sample = tone_pct_q[k];
+			out->tone_quality_indicator[k] = tone_quality[k];
+		}
+	}
 }
 
 static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
@@ -2239,13 +2738,17 @@ static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
 
 static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
 			     enum cs_role remote_role, bool include_pct,
-			     uint8_t num_antenna_paths)
+			     uint8_t num_antenna_paths,
+			     struct cs_mode_three_data *out)
 {
+	struct cs_mode_one_data *out_m1 = out ? &out->mode_one_data : NULL;
+	struct cs_mode_two_data *out_m2 = out ? &out->mode_two_data : NULL;
+
 	/* Mode 3 = Mode 1 + Mode 2 */
-	parse_mode_one(rap, mode_iov, remote_role, include_pct);
+	parse_mode_one(rap, mode_iov, remote_role, include_pct, out_m1);
 
 	if (mode_iov->iov_len > 0)
-		parse_mode_two(rap, mode_iov, num_antenna_paths);
+		parse_mode_two(rap, mode_iov, num_antenna_paths, out_m2);
 }
 
 static bool parse_subevent_header(struct iovec *iov,
@@ -2278,7 +2781,8 @@ static bool parse_subevent_header(struct iovec *iov,
 
 static bool parse_step(struct bt_rap *rap, struct iovec *iov,
 			struct cstracker *reqtracker,
-			uint8_t num_antenna_paths, uint8_t step_idx)
+			uint8_t num_antenna_paths, uint8_t step_idx,
+			struct cs_step_data *out_step)
 {
 	uint8_t mode_byte, step_mode;
 	bool include_pct;
@@ -2337,20 +2841,42 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
 	mode_iov.iov_base = payload;
 	mode_iov.iov_len = step_payload_len;
 
+	if (out_step) {
+		out_step->step_mode = step_mode;
+		out_step->step_chnl = 0;
+		out_step->step_data_length = (uint8_t)step_payload_len;
+	}
+
 	switch (step_mode) {
-	case CS_MODE_ZERO:
-		parse_mode_zero(rap, &mode_iov, remote_role);
+	case CS_MODE_ZERO: {
+		struct cs_mode_zero_data *out = out_step ?
+			&out_step->step_mode_data.mode_zero_data : NULL;
+
+		parse_mode_zero(rap, &mode_iov, remote_role, out);
 		break;
-	case CS_MODE_ONE:
-		parse_mode_one(rap, &mode_iov, remote_role, include_pct);
+	}
+	case CS_MODE_ONE: {
+		struct cs_mode_one_data *out = out_step ?
+			&out_step->step_mode_data.mode_one_data : NULL;
+
+		parse_mode_one(rap, &mode_iov, remote_role, include_pct, out);
 		break;
-	case CS_MODE_TWO:
-		parse_mode_two(rap, &mode_iov, num_antenna_paths);
+	}
+	case CS_MODE_TWO: {
+		struct cs_mode_two_data *out = out_step ?
+			&out_step->step_mode_data.mode_two_data : NULL;
+
+		parse_mode_two(rap, &mode_iov, num_antenna_paths, out);
 		break;
-	case CS_MODE_THREE:
+	}
+	case CS_MODE_THREE: {
+		struct cs_mode_three_data *out = out_step ?
+			&out_step->step_mode_data.mode_three_data : NULL;
+
 		parse_mode_three(rap, &mode_iov, remote_role, include_pct,
-						num_antenna_paths);
+					num_antenna_paths, out);
 		break;
+	}
 	default:
 		break;
 	}
@@ -2360,12 +2886,16 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
 
 static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
 				struct cstracker *reqtracker,
-				uint8_t num_antenna_paths, uint8_t num_steps)
+				uint8_t num_antenna_paths, uint8_t num_steps,
+				struct cs_step_data *out_steps)
 {
 	uint8_t i;
 
 	for (i = 0; i < num_steps; i++) {
-		if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
+		struct cs_step_data *out = out_steps ? &out_steps[i] : NULL;
+
+		if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i,
+				out))
 			break;
 	}
 }
@@ -2376,6 +2906,10 @@ static void parse_ras_data_segments(struct bt_rap *rap,
 	struct iovec iov;
 	uint8_t antenna_mask;
 	uint8_t num_antenna_paths;
+	uint16_t ranging_counter;
+	struct cs_proc_state *state = NULL;
+	struct bcs_procedure_data *bcs = NULL;
+	bool is_initiator;
 
 	if (!rap || !reqtracker)
 		return;
@@ -2387,10 +2921,30 @@ static void parse_ras_data_segments(struct bt_rap *rap,
 		ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
 	num_antenna_paths = antenna_mask_count_paths(antenna_mask);
 
+	ranging_counter =
+		ranging_header_get_counter(&reqtracker->ranging_header_);
+
+	/* Find the per-procedure state that matches this RAS counter */
+	if (rap->procedure_data_cb) {
+		state = find_proc_state_for_ras(reqtracker, ranging_counter);
+		if (state) {
+			bcs = &state->bcs_data;
+			bcs->reflector_selected_tx_power =
+				reqtracker->ranging_header_.selected_tx_power;
+		}
+	}
+
+	/* When local role=INITIATOR, remote data goes to
+	 * reflector_subevent_results. When local role=REFLECTOR,
+	 * remote data goes to initiator_subevent_results.
+	 */
+	is_initiator = (reqtracker->role == CS_ROLE_INITIATOR);
+
 	iov = reqtracker->segment_data;
 
 	while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
 		struct ras_subevent_header hdr;
+		struct cs_step_data *steps = NULL;
 
 		if (!parse_subevent_header(&iov, &hdr))
 			break;
@@ -2402,19 +2956,66 @@ static void parse_ras_data_segments(struct bt_rap *rap,
 		    hdr.reference_power_level,
 		    hdr.num_steps_reported);
 
+		if (bcs && hdr.num_steps_reported > 0)
+			steps = calloc(hdr.num_steps_reported, sizeof(*steps));
+
 		parse_subevent_steps(rap, &iov, reqtracker,
 					num_antenna_paths,
-					hdr.num_steps_reported);
+					hdr.num_steps_reported,
+					steps);
+
+		if (bcs) {
+			uint32_t stored_steps =
+					steps ? hdr.num_steps_reported : 0;
+
+			if (is_initiator)
+				bcs_proc_data_add_reflector_subevent(bcs,
+					hdr.start_acl_conn_event,
+					hdr.frequency_compensation,
+					hdr.reference_power_level,
+					num_antenna_paths,
+					hdr.subevent_abort_reason,
+					0,
+					steps, stored_steps);
+			else
+				bcs_proc_data_add_initiator_subevent(bcs,
+					hdr.start_acl_conn_event,
+					hdr.frequency_compensation,
+					hdr.reference_power_level,
+					num_antenna_paths,
+					hdr.subevent_abort_reason,
+					0,
+					steps, stored_steps);
+			steps = NULL; /* ownership transferred */
+		} else {
+			free(steps);
+			steps = NULL;
+		}
 
-		if (hdr.subevent_done_status ==
-		    SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
-		    hdr.ranging_done_status ==
+		if (state) {
+			if (hdr.subevent_done_status ==
+			    SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
+				state->remote_has_complete_subevent = true;
+			if (hdr.ranging_done_status ==
+			    RANGING_DONE_ALL_RESULTS_COMPLETE)
+				state->remote_status =
+						CS_PROC_ALL_RESULTS_COMPLETE;
+		}
+
+		if (bcs && hdr.ranging_abort_reason)
+			bcs->reflector_procedure_abort_reason =
+					hdr.ranging_abort_reason & 0x0F;
+
+		if (hdr.ranging_done_status ==
 		    RANGING_DONE_ALL_RESULTS_COMPLETE) {
 			DBG(rap, "Ranging procedure complete");
 			break;
 		}
 	}
 
+	if (state)
+		check_cs_procedure_complete(rap, reqtracker, state);
+
 	free(reqtracker->segment_data.iov_base);
 	reqtracker->segment_data.iov_base = NULL;
 	reqtracker->segment_data.iov_len = 0;
diff --git a/src/shared/rap.h b/src/shared/rap.h
index 5582635e6..342b504ea 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -157,10 +157,45 @@ struct rap_ev_cs_subevent_result_cont {
 	struct cs_step_data step_data[];
 };
 
+struct cs_subevent_result_data {
+	uint16_t             start_acl_conn_evt_counter;
+	uint16_t             freq_comp;
+	int8_t               ref_pwr_lvl;
+	uint8_t              num_ant_paths;
+	uint8_t              subevent_abort_reason;
+	uint64_t             timestamp_nanos;
+	uint32_t             num_steps;
+	struct cs_step_data *step_data;
+};
+
+struct bcs_procedure_data {
+	uint16_t procedure_counter;
+	uint16_t procedure_sequence;
+
+	int8_t   initiator_selected_tx_power;
+	int8_t   reflector_selected_tx_power;
+
+	struct cs_subevent_result_data *initiator_subevent_results;
+	uint32_t initiator_subevent_count;
+	uint8_t  initiator_procedure_abort_reason;
+
+	struct cs_subevent_result_data *reflector_subevent_results;
+	uint32_t reflector_subevent_count;
+	uint8_t  reflector_procedure_abort_reason;
+
+	struct rap_ev_cs_proc_enable_cmplt proc_enable_config;
+	struct rap_ev_cs_config_cmplt      cs_config;
+	uint8_t  t_sw_time_us_supported_by_local;
+	uint8_t  t_sw_time_us_supported_by_remote;
+	uint16_t ble_conn_interval;
+};
 typedef void (*bt_rap_debug_func_t)(const char *str, void *user_data);
 typedef void (*bt_rap_ready_func_t)(struct bt_rap *rap, void *user_data);
 typedef void (*bt_rap_destroy_func_t)(void *user_data);
 typedef void (*bt_rap_func_t)(struct bt_rap *rap, void *user_data);
+typedef void (*bt_rap_procedure_data_func_t)(struct bt_rap *rap,
+					struct bcs_procedure_data *data,
+					void *user_data);
 
 struct bt_rap *bt_rap_ref(struct bt_rap *rap);
 void bt_rap_unref(struct bt_rap *rap);
@@ -177,6 +212,10 @@ bool bt_rap_set_user_data(struct bt_rap *rap, void *user_data);
 bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 			void *user_data, bt_rap_destroy_func_t destroy);
 
+bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
+				bt_rap_procedure_data_func_t cb,
+				void *user_data,
+				bt_rap_destroy_func_t destroy);
 /* session related functions */
 unsigned int bt_rap_register(bt_rap_func_t attached, bt_rap_func_t detached,
 					void *user_data);
@@ -228,6 +267,10 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
 			int8_t max_tx_power);
 void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
 
+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);
 /* Connection handle mapping functions */
 bool bt_rap_set_conn_hndl(void *hci_sm,
 			struct bt_rap *rap,
@@ -245,3 +288,10 @@ void bt_rap_set_timeout_cb(void *hci_sm, void (*func)(void *),
 
 void bt_rap_set_proc_active_cb(void *hci_sm, void (*func)(bool, void *),
 				void *user_data);
+
+/* CS capability sw_time and connection interval setters */
+void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time);
+
+void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time);
+
+void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval);
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v1 0/2] Channel Sounding: Aggregate procedure data and expose via D-Bus
From: Prathibha Madugonde @ 2026-07-17  7:26 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

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

This series adds infrastructure to aggregate Channel Sounding (CS)
procedure results from both the local initiator and remote reflector
into a single bcs_procedure_data structure, and exposes the completed
data over D-Bus for use by distance-calculation applications.

Patch overview:
  1/2 shared/rap: Add bcs_procedure_data aggregation and procedure data API
  2/2 profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding

Prathibha Madugonde (2):
  shared/rap: Add bcs_procedure_data aggregation and procedure data API
  profiles/ranging: Add D-Bus ProcedureData signal for Channel Sounding

 profiles/ranging/rap.c     | 497 ++++++++++++++++++++++++++-
 profiles/ranging/rap_hci.c |  59 ++++
 src/shared/rap.c           | 673 +++++++++++++++++++++++++++++++++++--
 src/shared/rap.h           |  50 +++
 4 files changed, 1242 insertions(+), 37 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH v14 7/7] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

Add support for MT7928 (internal device ID is MT7935) which requires
additional firmware (CBMCU firmware) loading before Bluetooth firmware.

CBMCU is a new component on MT7928 to handle common part shared across
the combo chip (Wi-Fi/Bluetooth's subsystem), providing a better user
experience through improved coordination between subsystems.

Implement two-phase CBMCU firmware download: Phase 1 loads section with
type 0x5 containing global descriptor, section maps and signature data;
Phase 2 loads remaining firmware sections. Add retry mechanism for
concurrent download protection.

After CBMCU firmware loads successfully, the driver continues to load
corresponding BT firmware based on device ID through fallthrough to
case 0x7922/0x7925.

Use %.16s format specifier for hdr->datetime field in CBMCU firmware
logging to prevent potential buffer over-read, as the field is a 16-byte
array that may not be null-terminated.

Add integer overflow protection when calculating cert_len to prevent
heap buffer overflow during CBMCU Phase 1 firmware download.

The firmware(CBMCU_CODE_MT7935_1_1.bin/BT_RAM_CODE_MT7935_1_1_hdr.bin)
required for MT7928 will be scheduled for upload to linux-firmware at
a later stage.

MT7928 bring-up kernel log:
[ 6931.197167] usb 1-3: New USB device found, idVendor=0e8d, idProduct=7935, bcdDevice= 1.00
[ 6931.197212] usb 1-3: New USB device strings: Mfr=5, Product=6, SerialNumber=7
[ 6931.197237] usb 1-3: Product: Wireless_Device
[ 6931.197258] usb 1-3: Manufacturer: MediaTek Inc.
[ 6931.197279] usb 1-3: SerialNumber: 000000000
[ 6931.213478] Bluetooth: hci1: Loading CBMCU firmware: mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin
[ 6931.215214] Bluetooth: hci1: CBMCU HW ver: 0x7935, SW ver: 0x0000, Build Time: 20260601T161751+
[ 6931.623962] Bluetooth: hci1: CBMCU firmware download completed
[ 6931.643916] Bluetooth: hci1: Loading BT firmware: mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin
[ 6931.650467] Bluetooth: hci1: BT HW ver: 0x7935, SW ver: 0x0000, Build Time: 20260527000816
[ 6935.039790] Bluetooth: hci1: Device setup in 3369644 usecs
[ 6935.039833] Bluetooth: hci1: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
[ 6935.160654] Bluetooth: hci1: AOSP extensions version v2.00
[ 6935.160710] Bluetooth: hci1: AOSP quality report is supported
[ 6935.162954] Bluetooth: MGMT ver 1.23

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtk.c | 383 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btmtk.h |   3 +
 2 files changed, 385 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index e059e72d9dbe..9e17c72d7f40 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -21,6 +21,12 @@
 #define MTK_FW_ROM_PATCH_SEC_MAP_SIZE	64
 #define MTK_SEC_MAP_COMMON_SIZE	12
 #define MTK_SEC_MAP_NEED_SEND_SIZE	52
+#define MTK_SEC_MAP_LENGTH_SIZE	4
+#define MTK_SEC_CBMCU_DESC	0x5
+
+/* CBMCU WMT command flags */
+#define BTMTK_CBMCU_FLAG_QUERY_STATUS	0xF0
+#define BTMTK_CBMCU_FLAG_ENABLE_PATCH	0xF1

 /* It is for mt79xx iso data transmission setting */
 #define MTK_ISO_THRESHOLD	264
@@ -120,6 +126,11 @@ void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
 		snprintf(buf, size,
 			 "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
 			 dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1);
+	/* MT7928 */
+	else if (dev_id == 0x7935)
+		snprintf(buf, size,
+			 "mediatek/mt7928/BT_RAM_CODE_MT%04x_1_1_hdr.bin",
+			 dev_id & 0xffff);
 	else if (dev_id == 0x7961 && fw_flavor)
 		snprintf(buf, size,
 			 "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
@@ -775,6 +786,7 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
 			status = BTMTK_WMT_ON_UNDONE;
 		break;
 	case BTMTK_WMT_PATCH_DWNLD:
+	case BTMTK_WMT_CBMCU_DWNLD:
 		if (wmt_evt->whdr.flag == 2)
 			status = BTMTK_WMT_PATCH_DONE;
 		else if (wmt_evt->whdr.flag == 1)
@@ -911,6 +923,364 @@ static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
 	return val & MTK_BT_RST_DONE;
 }

+static int btmtk_cbmcu_patch_status(struct hci_dev *hdev,
+				    wmt_cmd_sync_func_t wmt_cmd_sync,
+				    u8 *patch_status)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	int status, err, retry = 20;
+
+	do {
+		wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+		wmt_params.flag = BTMTK_CBMCU_FLAG_QUERY_STATUS;
+		wmt_params.dlen = 0;
+		wmt_params.data = NULL;
+		wmt_params.status = &status;
+
+		err = wmt_cmd_sync(hdev, &wmt_params);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to query CBMCU patch status (%d)", err);
+			return err;
+		}
+
+		*patch_status = (u8)status;
+
+		if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
+			msleep(100);
+			retry--;
+		} else {
+			break;
+		}
+	} while (retry > 0);
+
+	if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
+		bt_dev_err(hdev, "CBMCU patch status query timeout");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int btmtk_query_cbmcu_section(struct hci_dev *hdev,
+				     wmt_cmd_sync_func_t wmt_cmd_sync,
+				     u8 cbmcu_type,
+				     const u8 *section_map,
+				     u32 cert_len)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	u8 cmd[64];
+	int status, err;
+
+	cmd[0] = 0;
+	cmd[1] = cbmcu_type;
+
+	if (cbmcu_type == 0)
+		put_unaligned_le32(cert_len, &cmd[2]);
+	else
+		memcpy(&cmd[2], section_map, MTK_SEC_MAP_NEED_SEND_SIZE);
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.flag = 0;
+	wmt_params.dlen = cbmcu_type ?
+		MTK_SEC_MAP_NEED_SEND_SIZE + 2 :
+		MTK_SEC_MAP_LENGTH_SIZE + 2;
+	wmt_params.data = cmd;
+	wmt_params.status = &status;
+
+	err = wmt_cmd_sync(hdev, &wmt_params);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to query CBMCU section (%d)", err);
+		return err;
+	}
+
+	/* Query should return UNDONE status for successful section query */
+	if (status != BTMTK_WMT_PATCH_UNDONE) {
+		bt_dev_err(hdev, "CBMCU section query status error (%d)", status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int btmtk_download_cbmcu_section(struct hci_dev *hdev,
+					wmt_cmd_sync_func_t wmt_cmd_sync,
+					const u8 *fw_data,
+					u32 dl_size)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	u32 sent_len, total_size = dl_size;
+	int err;
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.status = NULL;
+
+	while (dl_size > 0) {
+		sent_len = min_t(u32, 250, dl_size);
+
+		if (dl_size == total_size)
+			wmt_params.flag = BTMTK_WMT_PKT_START;
+		else if (dl_size == sent_len)
+			wmt_params.flag = BTMTK_WMT_PKT_END;
+		else
+			wmt_params.flag = BTMTK_WMT_PKT_CONTINUE;
+
+		wmt_params.dlen = sent_len;
+		wmt_params.data = fw_data;
+
+		err = wmt_cmd_sync(hdev, &wmt_params);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to send CBMCU section data (%d)", err);
+			return err;
+		}
+
+		dl_size -= sent_len;
+		fw_data += sent_len;
+	}
+
+	return 0;
+}
+
+static int btmtk_enable_cbmcu_patch(struct hci_dev *hdev,
+				    wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	int err;
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.flag = BTMTK_CBMCU_FLAG_ENABLE_PATCH;
+	wmt_params.dlen = 0;
+	wmt_params.data = NULL;
+	wmt_params.status = NULL;
+
+	err = wmt_cmd_sync(hdev, &wmt_params);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to enable CBMCU patch (%d)", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int btmtk_load_cbmcu_firmware(struct hci_dev *hdev,
+				     const char *fwname,
+				     wmt_cmd_sync_func_t wmt_cmd_sync,
+				     u32 dev_id)
+{
+	struct btmtk_patch_header *hdr;
+	struct btmtk_global_desc *globaldesc;
+	struct btmtk_section_map *sectionmap;
+	const struct firmware *fw;
+	const u8 *fw_ptr;
+	u8 *cert_buf = NULL;
+	u32 section_num, section_offset, dl_size;
+	size_t cert_len, expected_size, map_size, temp_size;
+	int i, err;
+
+	err = request_firmware(&fw, fwname, &hdev->dev);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to load CBMCU firmware file %s (%d)",
+			   fwname, err);
+		return err;
+	}
+
+	if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
+		bt_dev_err(hdev, "CBMCU firmware too small: size=%zu, min=%u",
+			   fw->size,
+			   MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	fw_ptr = fw->data;
+	hdr = (struct btmtk_patch_header *)fw_ptr;
+	globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
+	section_num = le32_to_cpu(globaldesc->section_num);
+
+	/* Check for potential integer overflow in size calculation */
+	if (check_mul_overflow((size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE,
+			       (size_t)section_num, &expected_size) ||
+	    check_add_overflow(expected_size,
+			       (size_t)(MTK_FW_ROM_PATCH_HEADER_SIZE +
+					MTK_FW_ROM_PATCH_GD_SIZE),
+			       &expected_size)) {
+		bt_dev_err(hdev, "CBMCU firmware size calculation overflow (section_num=%u)",
+			   section_num);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	if (fw->size < expected_size) {
+		bt_dev_err(hdev, "CBMCU firmware truncated: size=%zu, expected=%zu (section_num=%u)",
+			   fw->size, expected_size, section_num);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	bt_dev_info(hdev, "CBMCU HW ver: 0x%04x, SW ver: 0x%04x, Build Time: %.16s",
+		    dev_id & 0xffff, le16_to_cpu(hdr->swver), hdr->datetime);
+
+	/* Phase 1: Download section type MTK_SEC_CBMCU_DESC */
+	for (i = 0; i < section_num; i++) {
+		sectionmap = (struct btmtk_section_map *)
+			(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+			 MTK_FW_ROM_PATCH_GD_SIZE +
+			 MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+		/* Only process MTK_SEC_CBMCU_DESC section in Phase 1 */
+		if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) != MTK_SEC_CBMCU_DESC)
+			continue;
+
+		section_offset = le32_to_cpu(sectionmap->secoffset);
+		dl_size = le32_to_cpu(sectionmap->secsize);
+
+		if (dl_size == 0)
+			continue;
+
+		if (section_offset > fw->size ||
+		    dl_size > fw->size - section_offset) {
+			bt_dev_err(hdev, "CBMCU Phase 1 section out of bounds");
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
+		/* Calculate cert_len with overflow protection */
+		if (check_mul_overflow((size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE,
+				       (size_t)section_num, &map_size) ||
+		    check_add_overflow(map_size,
+				       (size_t)MTK_FW_ROM_PATCH_GD_SIZE,
+				       &temp_size) ||
+		    check_add_overflow(temp_size, (size_t)dl_size, &cert_len)) {
+			bt_dev_err(hdev, "CBMCU Phase 1 cert_len calculation overflow (section_num=%u, dl_size=%u)",
+				   section_num, dl_size);
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
+		/* Query cbmcu section */
+		err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 0, NULL,
+						cert_len);
+		if (err < 0)
+			goto err_release_fw;
+
+		cert_buf = kmalloc(cert_len, GFP_KERNEL);
+		if (!cert_buf) {
+			err = -ENOMEM;
+			goto err_release_fw;
+		}
+
+		/* Copy Global Descriptor + All Section Maps */
+		memcpy(cert_buf,
+		       fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE,
+		       MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num);
+
+		/* Copy Phase 1 section data */
+		memcpy(cert_buf + MTK_FW_ROM_PATCH_GD_SIZE +
+		       MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
+		       fw_ptr + section_offset,
+		       dl_size);
+
+		/* Download Phase 1 section */
+		err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+						   cert_buf, cert_len);
+		kfree(cert_buf);
+		cert_buf = NULL;
+
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to download CBMCU Phase 1 section (%d)", err);
+			goto err_release_fw;
+		}
+
+		break;
+	}
+
+	/* Phase 2: Download other sections (type != MTK_SEC_CBMCU_DESC) */
+	for (i = 0; i < section_num; i++) {
+		sectionmap = (struct btmtk_section_map *)
+			(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+			 MTK_FW_ROM_PATCH_GD_SIZE +
+			 MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+		/* Skip MTK_SEC_CBMCU_DESC section in Phase 2 */
+		if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) == MTK_SEC_CBMCU_DESC)
+			continue;
+
+		section_offset = le32_to_cpu(sectionmap->secoffset);
+		dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
+
+		if (dl_size == 0)
+			continue;
+
+		if (section_offset > fw->size ||
+		    dl_size > fw->size - section_offset) {
+			bt_dev_err(hdev, "CBMCU Phase 2 section %d out of bounds", i);
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
+		/* Query cbmcu section */
+		err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 1,
+						(u8 *)&sectionmap->bin_info_spec,
+						0);
+		if (err < 0)
+			goto err_release_fw;
+
+		/* Download section data */
+		err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+						   fw_ptr + section_offset,
+						   dl_size);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to download CBMCU section %d (%d)", i, err);
+			goto err_release_fw;
+		}
+	}
+
+	bt_dev_info(hdev, "CBMCU firmware download completed");
+
+err_release_fw:
+	release_firmware(fw);
+	return err;
+}
+
+static int btmtk_setup_cbmcu_firmware(struct hci_dev *hdev,
+				      wmt_cmd_sync_func_t wmt_cmd_sync,
+				      u32 dev_id)
+{
+	char cbmcu_fwname[64];
+	u8 patch_status;
+	int err;
+
+	err = btmtk_cbmcu_patch_status(hdev, wmt_cmd_sync, &patch_status);
+	if (err < 0)
+		return err;
+
+	bt_dev_dbg(hdev, "CBMCU patch status: 0x%02x", patch_status);
+
+	if (patch_status != BTMTK_WMT_PATCH_UNDONE)
+		return 0;
+
+	snprintf(cbmcu_fwname, sizeof(cbmcu_fwname),
+		 "mediatek/mt7928/CBMCU_CODE_MT%04x_1_1.bin",
+		 dev_id & 0xffff);
+
+	bt_dev_info(hdev, "Loading CBMCU firmware: %s", cbmcu_fwname);
+
+	err = btmtk_load_cbmcu_firmware(hdev, cbmcu_fwname, wmt_cmd_sync, dev_id);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to download CBMCU firmware (%d)", err);
+		return err;
+	}
+
+	err = btmtk_enable_cbmcu_patch(hdev, wmt_cmd_sync);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
 {
 	u32 val;
@@ -935,7 +1305,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
 		if (err < 0)
 			return err;
 		msleep(100);
-	} else if (dev_id == 0x7925 || dev_id == 0x6639) {
+	} else if (dev_id == 0x7925 || dev_id == 0x6639 || dev_id == 0x7935) {
 		err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
 		if (err < 0)
 			return err;
@@ -1420,6 +1790,15 @@ int btmtk_usb_setup(struct hci_dev *hdev)
 	case 0x7668:
 		fwname = FIRMWARE_MT7668;
 		break;
+	case 0x7935:
+		/* Requires CBMCU firmware before BT firmware */
+		err = btmtk_setup_cbmcu_firmware(hdev, btmtk_usb_hci_wmt_sync,
+						 dev_id);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to set up CBMCU firmware (%d)", err);
+			return err;
+		}
+		fallthrough;
 	case 0x7922:
 	case 0x7925:
 		/*
@@ -1637,3 +2016,5 @@ MODULE_FIRMWARE(FIRMWARE_MT7922);
 MODULE_FIRMWARE(FIRMWARE_MT7961);
 MODULE_FIRMWARE(FIRMWARE_MT7925);
 MODULE_FIRMWARE(FIRMWARE_MT7927);
+MODULE_FIRMWARE(FIRMWARE_MT7928);
+MODULE_FIRMWARE(FIRMWARE_MT7928_CBMCU);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c234e5ea27e5..2d087d552d31 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -9,6 +9,8 @@
 #define FIRMWARE_MT7961		"mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
 #define FIRMWARE_MT7925		"mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
 #define FIRMWARE_MT7927		"mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
+#define FIRMWARE_MT7928		"mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin"
+#define FIRMWARE_MT7928_CBMCU	"mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin"

 #define HCI_EV_WMT 0xe4
 #define HCI_WMT_MAX_EVENT_SIZE		64
@@ -54,6 +56,7 @@ enum {
 	BTMTK_WMT_RST = 0x7,
 	BTMTK_WMT_REGISTER = 0x8,
 	BTMTK_WMT_SEMAPHORE = 0x17,
+	BTMTK_WMT_CBMCU_DWNLD = 0x58,
 };

 enum {
--
2.45.2


^ permalink raw reply related

* [PATCH v14 2/7] Bluetooth: btmtk: Add firmware size validation in btmtk_setup_firmware_79xx()
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

Add firmware size validation to prevent out-of-bounds access when loading
truncated or malicious firmware files.

Add three levels of validation:
1. Minimum size check for header and global descriptor
2. Section map bounds check with integer overflow protection using
   check_mul_overflow() and check_add_overflow()
3. Section data bounds check before accessing each section

This matches the validation approach used in btmtk_load_cbmcu_firmware().

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtk.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 02a96342e964..3491060b3ae9 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -145,6 +145,7 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 	int err, dlen, i, status;
 	u8 flag, first_block, retry;
 	u32 section_num, dl_size, section_offset;
+	size_t expected_size;
 	u8 cmd[64];
 
 	err = request_firmware(&fw, fwname, &hdev->dev);
@@ -153,12 +154,40 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 		return err;
 	}
 
+	/* Validate minimum firmware size for header and global descriptor */
+	if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
+		bt_dev_err(hdev, "Firmware file too small: size=%zu, expected at least %u bytes",
+			   fw->size, MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
 	fw_ptr = fw->data;
 	fw_bin_ptr = fw_ptr;
 	hdr = (struct btmtk_patch_header *)fw_ptr;
 	globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
 	section_num = le32_to_cpu(globaldesc->section_num);
 
+	/* Check for potential integer overflow in size calculation */
+	if (check_mul_overflow((size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE,
+			       (size_t)section_num, &expected_size) ||
+	    check_add_overflow(expected_size,
+			       (size_t)(MTK_FW_ROM_PATCH_HEADER_SIZE +
+					MTK_FW_ROM_PATCH_GD_SIZE),
+			       &expected_size)) {
+		bt_dev_err(hdev, "Firmware size calculation overflow (section_num=%u)",
+			   section_num);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	if (fw->size < expected_size) {
+		bt_dev_err(hdev, "Firmware truncated: size=%zu, expected=%zu (section_num=%u)",
+			   fw->size, expected_size, section_num);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
 	bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
 		    le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
 
@@ -171,6 +200,16 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 		section_offset = le32_to_cpu(sectionmap->secoffset);
 		dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
 
+		/* Validate section boundaries to prevent out-of-bounds access */
+		if (dl_size > 0 &&
+		    (section_offset > fw->size ||
+		     dl_size > fw->size - section_offset)) {
+			bt_dev_err(hdev, "Section %d out of bounds: offset=%u, size=%u, fw_size=%zu",
+				   i, section_offset, dl_size, fw->size);
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
 		/* MT6639: only download sections where dlmode byte0 == 0x01,
 		 * matching the Windows driver behavior which skips WiFi/other
 		 * sections that would cause the chip to hang.
-- 
2.45.2


^ permalink raw reply related

* [PATCH v14 4/7] Bluetooth: btmtksdio: Remove redundant firmware filename override
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

btmtksdio_setup() calls btmtk_fw_get_filename() to generate the correct
firmware filename based on device ID and version, then immediately
overwrites it with snprintf() using a generic legacy format.

This redundant override causes newer chips (MT6639, MT7925, MT7928) to
request incorrect firmware filenames, leading to firmware load failures.
For example, MT6639 needs "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
but snprintf() generates "mediatek/BT_RAM_CODE_MT6639_1_1_hdr.bin".

Remove the redundant snprintf() override to use the correct filename
generated by btmtk_fw_get_filename(), matching USB driver behavior.

Fixes: 7f935b21bee4 ("Bluetooth: btmtk: apply the common btmtk_fw_get_filename")
Signed-off-by: Chris Lu <chris.lu@mediatek.com>
---
 drivers/bluetooth/btmtksdio.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 1fdc24545e41..d895292103bb 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -1175,9 +1175,6 @@ static int btmtksdio_setup(struct hci_dev *hdev)
 		btmtk_fw_get_filename(fwname, sizeof(fwname), dev_id,
 				      fw_version, 0);
 
-		snprintf(fwname, sizeof(fwname),
-			 "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
-			 dev_id & 0xffff, (fw_version & 0xff) + 1);
 		err = mt79xx_setup(hdev, fwname, dev_id);
 		if (err < 0)
 			return err;
-- 
2.45.2


^ permalink raw reply related

* [PATCH v14 5/7] Bluetooth: btmtk: Improve BT firmware logging
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

Improve firmware loading log messages to provide more useful information:

- Add firmware filename before loading to help identify which file
  is being loaded
- Display chip ID (dev_id) as HW version instead of firmware's hwver
  field, which provides more meaningful hardware identification
- Use %.16s format specifier for hdr->datetime field to prevent
  potential buffer over-read, as the field is a 16-byte array that
  may not be null-terminated

log output with MT7922
[  212.878783] Bluetooth: hci1: Loading BT firmware: mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin
[  212.889614] Bluetooth: hci1: BT HW ver: 0x7922, SW ver: 0x008a, Build Time: 20260224103448
[  216.048877] Bluetooth: hci1: Device setup in 3096530 usecs
[  216.048890] Bluetooth: hci1: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
[  216.114179] Bluetooth: hci1: AOSP extensions version v1.00
[  216.114220] Bluetooth: hci1: AOSP quality report is supported
[  216.116782] Bluetooth: MGMT ver 1.23

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtk.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 3491060b3ae9..0fd6ce09a313 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -154,6 +154,8 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 		return err;
 	}
 
+	bt_dev_info(hdev, "Loading BT firmware: %s", fwname);
+
 	/* Validate minimum firmware size for header and global descriptor */
 	if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
 		bt_dev_err(hdev, "Firmware file too small: size=%zu, expected at least %u bytes",
@@ -188,8 +190,8 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 		goto err_release_fw;
 	}
 
-	bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
-		    le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
+	bt_dev_info(hdev, "BT HW ver: 0x%04x, SW ver: 0x%04x, Build Time: %.16s",
+		    dev_id & 0xffff, le16_to_cpu(hdr->swver), hdr->datetime);
 
 	for (i = 0; i < section_num; i++) {
 		first_block = 1;
-- 
2.45.2


^ permalink raw reply related

* [PATCH v14 0/7] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu

This patch series adds support for MT7928 (device ID 0x7935) to the
btmtk driver, which requires a new two-stage firmware loading process
with CBMCU firmware.

Patch 1 fixes SKB handling issues: DMA out-of-bounds read and cloned
SKB corruption. Fix by calling skb_unshare() and expanding skb tailroom
with zero-padding.

Patch 2 adds firmware size validation with integer overflow protection
to prevent out-of-bounds access with malicious firmware files.

Patch 3 passes hardware dev_id to mt79xx_setup() to fix incorrect chip
ID logging (was hardcoded to 0).

Patch 4 removes redundant snprintf() that overrides btmtk_fw_get_filename()
result, causing newer chips to request incorrect firmware filenames.

Patch 5 improves firmware logging: adds filename log, uses chip ID as
HW version, and uses %.16s format specifier for datetime field.

Patch 6 replaces magic numbers with BTMTK_WMT_PKT_* enum for better
code readability.

Patch 7 implements MT7928 CBMCU firmware download with two-phase loading
sequence and cert_len overflow protection.

Tested on MT7928 hardware with successful firmware loading and
Bluetooth functionality verification.

Changes in v14:
- Rebased onto latest bluetooth-next tree
- No functional changes from v13

Changes in v13:
- Fix cloned SKB corruption (HIGH severity) by adding skb_unshare() (Patch 1)
- Fix cert_len integer overflow (CRITICAL severity) in CBMCU firmware loading (Patch 7)

Pre-existing issues fixed in subsequent patches:
- Patch 2 introduces format string issue (%s on non-null-terminated field)
  -> Fixed in Patch 5 using %.16s format specifier
- Patch 3 context has pre-existing snprintf() override (identified in v12 review)
  -> Fixed in Patch 4, resolving firmware load failures on MT6639/MT7925/MT7928
This gradual improvement approach allows independent backporting of each fix.

v12 -> v13:
- Added skb_unshare() to prevent cloned SKB corruption
- Added overflow checks for cert_len calculation
- Split SDIO firmware loading fixes into two patches

Changes in v12:
- Reordered patches to fix dependency: move "Pass dev_id" before
  "Improve logging" so dev_id is available when logging

Changes in v11:
- Fixed use-after-free: reassign sdio_hdr after pskb_expand_head()
- Reordered patches for bisectability

Changes in v10:
- Added Patch 1 to fix DMA out-of-bounds access

Changes in v9:
- Reordered patches to group SDIO fixes together

Changes in v8:
- Split firmware validation from dev_id pass-through into separate patches

Changes in v7:
- Added Fixes tag per feedback

Changes in v6:
- Added Fixes tag and restructured parameter comment

Changes in v5:
- Moved documentation to function-level comment

Changes in v4:
- Fixed commit message and expanded description

Changes in v3:
- Changed dev_id type from u16 to u32

Changes in v2:
- Combined MT7928 functionality into single patch

Chris Lu (7):
  Bluetooth: btmtksdio: Fix SKB handling issues in TX path
  Bluetooth: btmtk: Add firmware size validation in
    btmtk_setup_firmware_79xx()
  Bluetooth: btmtksdio: Pass hardware dev_id to mt79xx_setup()
  Bluetooth: btmtksdio: Remove redundant firmware filename override
  Bluetooth: btmtk: Improve BT firmware logging
  Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
  Bluetooth: btmtk: Add MT7928 support

 drivers/bluetooth/btmtk.c     | 427 +++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btmtk.h     |   9 +
 drivers/bluetooth/btmtksdio.c |  55 ++++-
 3 files changed, 471 insertions(+), 20 deletions(-)

-- 
2.45.2


^ permalink raw reply

* [PATCH v14 6/7] Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

Add BTMTK_WMT_PKT_* enum to represent WMT download packet sequence flags,
improving code readability. Replace magic numbers (1, 2, 3) in
btmtk_setup_firmware_79xx() and btmtk_setup_firmware() with descriptive
enum values:

- BTMTK_WMT_PKT_START (1): First packet of a sequence
- BTMTK_WMT_PKT_CONTINUE (2): Continuation packet
- BTMTK_WMT_PKT_END (3): Final packet of a sequence

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtk.c | 12 ++++++------
 drivers/bluetooth/btmtk.h |  6 ++++++
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 0fd6ce09a313..e059e72d9dbe 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -271,12 +271,12 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 			while (dl_size > 0) {
 				dlen = min_t(int, 250, dl_size);
 				if (first_block == 1) {
-					flag = 1;
+					flag = BTMTK_WMT_PKT_START;
 					first_block = 0;
 				} else if (dl_size - dlen <= 0) {
-					flag = 3;
+					flag = BTMTK_WMT_PKT_END;
 				} else {
-					flag = 2;
+					flag = BTMTK_WMT_PKT_CONTINUE;
 				}
 
 				wmt_params.flag = flag;
@@ -355,7 +355,7 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
 
 	fw_size -= 30;
 	fw_ptr += 30;
-	flag = 1;
+	flag = BTMTK_WMT_PKT_START;
 
 	wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
 	wmt_params.status = NULL;
@@ -365,9 +365,9 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
 
 		/* Tell device the position in sequence */
 		if (fw_size - dlen <= 0)
-			flag = 3;
+			flag = BTMTK_WMT_PKT_END;
 		else if (fw_size < fw->size - 30)
-			flag = 2;
+			flag = BTMTK_WMT_PKT_CONTINUE;
 
 		wmt_params.flag = flag;
 		wmt_params.dlen = dlen;
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..c234e5ea27e5 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -66,6 +66,12 @@ enum {
 	BTMTK_WMT_ON_PROGRESS,
 };
 
+enum btmtk_wmt_pkt_flag {
+	BTMTK_WMT_PKT_START = 1,
+	BTMTK_WMT_PKT_CONTINUE = 2,
+	BTMTK_WMT_PKT_END = 3,
+};
+
 struct btmtk_wmt_hdr {
 	u8	dir;
 	u8	op;
-- 
2.45.2


^ permalink raw reply related

* [PATCH v14 3/7] Bluetooth: btmtksdio: Pass hardware dev_id to mt79xx_setup()
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

mt79xx_setup() hardcodes dev_id=0 when calling btmtk_setup_firmware_79xx(),
causing SDIO devices to log incorrect chip ID "BT HW ver: 0x0000" instead
of the actual hardware version read from register 0x70010200.

Pass the hardware dev_id to mt79xx_setup() and forward it to
btmtk_setup_firmware_79xx() to match USB driver behavior and display
correct chip identification.

Fixes: 28b7c5a6db74 ("Bluetooth: btmtk: Add MT6639 (MT7927) Bluetooth support")
Signed-off-by: Chris Lu <chris.lu@mediatek.com>
---
 drivers/bluetooth/btmtksdio.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 42cb0bbb720f..1fdc24545e41 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -912,14 +912,14 @@ static int mt76xx_setup(struct hci_dev *hdev, const char *fwname)
 	return 0;
 }
 
-static int mt79xx_setup(struct hci_dev *hdev, const char *fwname)
+static int mt79xx_setup(struct hci_dev *hdev, const char *fwname, u32 dev_id)
 {
 	struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
 	struct btmtk_hci_wmt_params wmt_params;
 	u8 param = 0x1;
 	int err;
 
-	err = btmtk_setup_firmware_79xx(hdev, fwname, mtk_hci_wmt_sync, 0);
+	err = btmtk_setup_firmware_79xx(hdev, fwname, mtk_hci_wmt_sync, dev_id);
 	if (err < 0) {
 		bt_dev_err(hdev, "Failed to setup 79xx firmware (%d)", err);
 		return err;
@@ -1178,7 +1178,7 @@ static int btmtksdio_setup(struct hci_dev *hdev)
 		snprintf(fwname, sizeof(fwname),
 			 "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
 			 dev_id & 0xffff, (fw_version & 0xff) + 1);
-		err = mt79xx_setup(hdev, fwname);
+		err = mt79xx_setup(hdev, fwname, dev_id);
 		if (err < 0)
 			return err;
 
-- 
2.45.2


^ permalink raw reply related

* [PATCH v14 1/7] Bluetooth: btmtksdio: Fix SKB handling issues in TX path
From: Chris Lu @ 2026-07-17  7:21 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, Paul Menzel,
	linux-bluetooth, linux-kernel, linux-mediatek, Chris Lu, Sashiko
In-Reply-To: <20260717072133.2858136-1-chris.lu@mediatek.com>

Fix two SKB handling issues in btmtksdio_tx_packet():

1. DMA out-of-bounds read: The driver aligns transfer size to 256 bytes
   using round_up(), but does not ensure the skb buffer has sufficient
   tailroom, causing DMA to read beyond the buffer. Fix by expanding skb
   tailroom if needed and zero-filling the padding.

2. Cloned SKB corruption: The driver modifies SKB header (via skb_push)
   and tail (via skb_put_zero) without checking if the SKB is cloned.
   If the Bluetooth core passes a cloned SKB (e.g., from L2CAP
   retransmission queues), the driver corrupts the shared data buffer
   for other subsystems. Fix by calling skb_unshare() at function entry
   to ensure we have a private copy before any modifications.

Fixes: 9aebfd4a2200 ("Bluetooth: mediatek: add support for MediaTek MT7663S and MT7668S SDIO devices")
Reported-by: Sashiko <sashiko-review@kernel.org>
Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Assisted-by: Claude:Sonnet-4.5
---
 drivers/bluetooth/btmtksdio.c | 46 +++++++++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index c6f80c419e90..42cb0bbb720f 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -272,9 +272,20 @@ static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev,
 			       struct sk_buff *skb)
 {
 	struct mtkbtsdio_hdr *sdio_hdr;
+	unsigned int padded_len, pad_len;
 	int err;
 
-	/* Make sure that there are enough rooms for SDIO header */
+	/* Ensure SKB is not cloned before any modification.
+	 * If cloned (e.g., L2CAP retransmission queues), create a
+	 * private copy to avoid corrupting shared data buffer.
+	 */
+	skb = skb_unshare(skb, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+
+	/* Make sure that there are enough rooms for SDIO header.
+	 * After skb_unshare(), we have exclusive ownership of the buffer.
+	 */
 	if (unlikely(skb_headroom(skb) < sizeof(*sdio_hdr))) {
 		err = pskb_expand_head(skb, sizeof(*sdio_hdr), 0,
 				       GFP_ATOMIC);
@@ -290,20 +301,45 @@ static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev,
 	sdio_hdr->reserved = cpu_to_le16(0);
 	sdio_hdr->bt_type = hci_skb_pkt_type(skb);
 
+	/* Calculate padded length for block-aligned DMA transfer.
+	 * SDIO requires transfers to be block-aligned (MTK_SDIO_BLOCK_SIZE).
+	 * Pad with zeros to prevent DMA from reading beyond skb buffer.
+	 */
+	padded_len = round_up(skb->len, MTK_SDIO_BLOCK_SIZE);
+	pad_len = padded_len - skb->len;
+
+	if (pad_len > 0) {
+		/* Ensure sufficient tailroom for padding */
+		if (unlikely(skb_tailroom(skb) < pad_len)) {
+			err = pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC);
+			if (err < 0)
+				goto err_skb_pull;
+			/* Reassign sdio_hdr after buffer reallocation */
+			sdio_hdr = (void *)skb->data;
+		}
+
+		/* Zero-fill padding to prevent information disclosure */
+		skb_put_zero(skb, pad_len);
+	}
+
 	clear_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);
-	err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data,
-			   round_up(skb->len, MTK_SDIO_BLOCK_SIZE));
+	err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data, padded_len);
 	if (err < 0)
-		goto err_skb_pull;
+		goto err_skb_trim;
 
-	bdev->hdev->stat.byte_tx += skb->len;
+	/* Record actual transmitted data (excluding padding) */
+	bdev->hdev->stat.byte_tx += le16_to_cpu(sdio_hdr->len);
 
 	kfree_skb(skb);
 
 	return 0;
 
+err_skb_trim:
+	if (pad_len > 0)
+		skb_trim(skb, skb->len - pad_len);
 err_skb_pull:
 	skb_pull(skb, sizeof(*sdio_hdr));
+	kfree_skb(skb);
 
 	return err;
 }
-- 
2.45.2


^ permalink raw reply related

* Re: [PATCH BlueZ v3] shared/rap: Add bcs_procedure_data aggregation and procedure data API
From: Prathibha Madugonde @ 2026-07-17  6:33 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <CABBYNZK1rOiY9a9DHiH4woYez=VQ+OOcD72kX9WYGET8mvJD3A@mail.gmail.com>



On 7/16/2026 10:28 PM, Luiz Augusto von Dentz wrote:
> Hi Prathibha,
> 
> On Thu, Jul 16, 2026 at 10:17 AM Prathibha Madugonde
> <prathibha.madugonde@oss.qualcomm.com> wrote:
>>
>> From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
>>
>> Define bcs_procedure_data to hold per-procedure CS results from both
>> the local initiator and remote reflector, including subevent step data,
>> selected TX powers, procedure enable config, CS config, sw_time values,
>> and BLE connection interval.
>>
>> Add cs_proc_state with a 16-entry ring buffer in cstracker to track
>> procedures keyed by procedure counter, accumulating results
>> independently from each side until both report all-results-complete.
>>
>> Update parse_mode_{0,1,2,3}, parse_step, and parse_subevent_steps with
>> output pointer parameters to capture remote reflector step data.
>> Introduce parse_cs_local_initiator_data() to store local HCI subevent
>> results, handling both initial and continuation events. Update
>> parse_ras_data_segments() to route decoded RAS subevent data into the
>> matching cs_proc_state as reflector results.
>>
>> Add bt_rap_set_procedure_data_cb() to register a callback fired once
>> both local and remote results are complete for a given procedure.
>> Add bt_rap_set_local_sw_time(), bt_rap_set_remote_sw_time(), and
>> bt_rap_set_conn_interval() so callers such as rap_hci.c can supply the
>> CS capability sw_time and connection interval required by upper-layer
>> distance calculation algorithms.
> 
> Does this mean the spec is incomplete or something? The ACL connection
> interval is known, but I have no idea what sw_time would be, is that a
> timestamp? And if it is how would that be transported if it is not
> part of RAP procedures?
> 
> If it is specific to the vendor algorithm it means it doesn't belong
> to bt_rap, or RAP does allows to carry vendor specific information?
> Even if does then we need to called what it is bt_rap_set_vendor_data,
> or something like that, but that would mean the other side needs to
> understand this information but I though the distance algorithm is
> something only the initiator has to deal with, if both side needs to
> negotiate this parameter it means they need to negotiate what
> algorithm to use as well.
> 

Hi Luiz,

Thank you for the review/comments.

The local & remote t_sw time is antenna switch period of the CS tones. 
It is spec defined as part of CS read local capabilities and remote 
capabilities.

Currently rap_hci.c already reads t_sw_time_supported in the 
local/remote capabilities-complete callbacks but only logs it — it isn't 
yet wired into bt_rap_set_local_sw_time()/bt_rap_set_remote_sw_time(). 
I'll add those calls in the respective command/event handlers and send 
in next patchset together.

>> Remove unwanted function rap_detached , fix for rap profile level
>> disconnections
>>
>> Changes in V2:
>>   Removed the block-scoped variable inside find_or_create_proc_state()
>>   Replaced the fixed 16-entry proc_states[] array with a struct queue *
>>    of heap-allocated cs_proc_state entries
>> ---
>>   src/shared/rap.c | 673 ++++++++++++++++++++++++++++++++++++++++++++---
>>   src/shared/rap.h |  47 ++++
>>   2 files changed, 684 insertions(+), 36 deletions(-)
>>
>> diff --git a/src/shared/rap.c b/src/shared/rap.c
>> index dfb272d3a..eadc41707 100644
>> --- a/src/shared/rap.c
>> +++ b/src/shared/rap.c
>> @@ -12,6 +12,8 @@
>>   #include <stdbool.h>
>>   #include <unistd.h>
>>   #include <errno.h>
>> +#include <time.h>
>> +#include <glib.h>
>>
>>   #include "bluetooth/bluetooth.h"
>>   #include "bluetooth/hci.h"
>> @@ -151,6 +153,16 @@ static inline uint8_t ranging_header_get_antenna_mask(
>>          return hdr->antenna_pct & 0x0F;
>>   }
>>
>> +static inline uint16_t ranging_header_get_counter(
>> +                                       const struct ranging_header *hdr)
>> +{
>> +       if (!hdr)
>> +               return 0;
>> +
>> +       return (uint16_t)(hdr->counter_config[0] |
>> +                         ((hdr->counter_config[1] & 0x0F) << 8));
>> +}
>> +
>>   static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
>>   {
>>          uint8_t count = 0;
>> @@ -206,11 +218,23 @@ enum cs_role {
>>   };
>>
>>   #define CS_INVALID_CONFIG_ID   0xFF
>> +#define CS_RANGING_COUNTER_MASK   0x0FFF
>> +
>>   /* Minimal enums (align to controller values if needed) */
>>   enum cs_procedure_done_status {
>>          CS_PROC_ALL_RESULTS_COMPLETE = 0x00,
>>          CS_PROC_PARTIAL_RESULTS      = 0x01,
>> -       CS_PROC_ABORTED              = 0x02
>> +       CS_PROC_ABORTED              = 0x0F
>> +};
>> +
>> +/* Per-procedure tracking — one entry per HCI procedure counter */
>> +struct cs_proc_state {
>> +       uint16_t proc_counter;
>> +       struct bcs_procedure_data bcs_data;
>> +       enum cs_procedure_done_status local_status;
>> +       bool                          local_has_complete_subevent;
>> +       enum cs_procedure_done_status remote_status;
>> +       bool                          remote_has_complete_subevent;
>>   };
>>
>>   /* Main cs_procedure_data  */
>> @@ -253,6 +277,12 @@ struct cstracker {
>>          struct ranging_header   ranging_header_;
>>          /* Client - subsequent segments appended using iovec */
>>          struct iovec segment_data;
>> +
>> +       /* Session-level config template and per-procedure state */
>> +       struct bcs_procedure_data     bcs_proc_data;
>> +       struct queue                  *proc_states;
>> +       uint8_t                       procedure_sequence_after_enable;
>> +       uint64_t                      proc_start_timestamp_nanos;
>>   };
>>
>>   /* Ranging Service context */
>> @@ -300,6 +330,15 @@ struct bt_rap {
>>          bt_rap_destroy_func_t debug_destroy;
>>          void *debug_data;
>>          void *user_data;
>> +
>> +       bt_rap_procedure_data_func_t  procedure_data_cb;
>> +       bt_rap_destroy_func_t         procedure_data_destroy;
>> +       void                         *procedure_data_user_data;
>> +
>> +       /* CS capabilities sw_time and connection interval for config param */
>> +       uint8_t local_sw_time;
>> +       uint8_t remote_sw_time;
>> +       uint16_t conn_interval;
>>          struct cstracker *resptracker;
>>          struct cstracker *reqtracker;
>>   };
>> @@ -542,6 +581,177 @@ static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
>>          return true;
>>   }
>>
>> +/* ---------- bcs_procedure_data helpers ----------------------------------- */
>> +static uint64_t get_time_nanos(void)
>> +{
>> +       struct timespec ts;
>> +
>> +       clock_gettime(CLOCK_REALTIME, &ts);
>> +       return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
>> +}
>> +
>> +static void bcs_proc_data_clear(struct bcs_procedure_data *proc)
>> +{
>> +       uint32_t i;
>> +
>> +       if (!proc)
>> +               return;
>> +
>> +       for (i = 0; i < proc->initiator_subevent_count; i++)
>> +               free(proc->initiator_subevent_results[i].step_data);
>> +       free(proc->initiator_subevent_results);
>> +       proc->initiator_subevent_results = NULL;
>> +       proc->initiator_subevent_count = 0;
>> +
>> +       for (i = 0; i < proc->reflector_subevent_count; i++)
>> +               free(proc->reflector_subevent_results[i].step_data);
>> +       free(proc->reflector_subevent_results);
>> +       proc->reflector_subevent_results = NULL;
>> +       proc->reflector_subevent_count = 0;
>> +}
>> +
>> +static bool bcs_proc_data_add_initiator_subevent(
>> +                                       struct bcs_procedure_data *proc,
>> +                                       uint16_t start_acl_evt,
>> +                                       uint16_t freq_comp,
>> +                                       int8_t ref_pwr,
>> +                                       uint8_t num_ant_paths,
>> +                                       uint8_t abort_reason,
>> +                                       uint64_t timestamp_nanos,
>> +                                       struct cs_step_data *steps,
>> +                                       uint32_t num_steps)
>> +{
>> +       struct cs_subevent_result_data *arr;
>> +       uint32_t new_count;
>> +
>> +       if (!proc)
>> +               return false;
>> +
>> +       new_count = proc->initiator_subevent_count + 1;
>> +       arr = realloc(proc->initiator_subevent_results,
>> +                                       new_count * sizeof(*arr));
>> +       if (!arr) {
>> +               free(steps);
>> +               return false;
>> +       }
>> +
>> +       arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
>> +       arr[new_count - 1].freq_comp = freq_comp;
>> +       arr[new_count - 1].ref_pwr_lvl = ref_pwr;
>> +       arr[new_count - 1].num_ant_paths = num_ant_paths;
>> +       arr[new_count - 1].subevent_abort_reason = abort_reason;
>> +       arr[new_count - 1].timestamp_nanos = timestamp_nanos;
>> +       arr[new_count - 1].num_steps = num_steps;
>> +       arr[new_count - 1].step_data = steps;
>> +
>> +       proc->initiator_subevent_results = arr;
>> +       proc->initiator_subevent_count = new_count;
>> +       return true;
>> +}
>> +
>> +static bool bcs_proc_data_add_reflector_subevent(
>> +                                       struct bcs_procedure_data *proc,
>> +                                       uint16_t start_acl_evt,
>> +                                       uint16_t freq_comp,
>> +                                       int8_t ref_pwr,
>> +                                       uint8_t num_ant_paths,
>> +                                       uint8_t abort_reason,
>> +                                       uint64_t timestamp_nanos,
>> +                                       struct cs_step_data *steps,
>> +                                       uint32_t num_steps)
>> +{
>> +       struct cs_subevent_result_data *arr;
>> +       uint32_t new_count;
>> +
>> +       if (!proc)
>> +               return false;
>> +
>> +       new_count = proc->reflector_subevent_count + 1;
>> +       arr = realloc(proc->reflector_subevent_results,
>> +                                       new_count * sizeof(*arr));
>> +       if (!arr) {
>> +               free(steps);
>> +               return false;
>> +       }
>> +
>> +       arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
>> +       arr[new_count - 1].freq_comp = freq_comp;
>> +       arr[new_count - 1].ref_pwr_lvl = ref_pwr;
>> +       arr[new_count - 1].num_ant_paths = num_ant_paths;
>> +       arr[new_count - 1].subevent_abort_reason = abort_reason;
>> +       arr[new_count - 1].timestamp_nanos = timestamp_nanos;
>> +       arr[new_count - 1].num_steps = num_steps;
>> +       arr[new_count - 1].step_data = steps;
>> +
>> +       proc->reflector_subevent_results = arr;
>> +       proc->reflector_subevent_count = new_count;
>> +       return true;
>> +}
>> +
>> +static bool match_proc_counter(const void *data, const void *match_data)
>> +{
>> +       const struct cs_proc_state *s = data;
>> +       uint16_t proc_counter = PTR_TO_UINT(match_data);
>> +
>> +       return s->proc_counter == proc_counter;
>> +}
>> +
>> +static void free_proc_state(void *data)
>> +{
>> +       struct cs_proc_state *s = data;
>> +
>> +       bcs_proc_data_clear(&s->bcs_data);
>> +       free(s);
>> +}
>> +
>> +static struct cs_proc_state *find_or_create_proc_state(struct cstracker *t,
>> +                                                       uint16_t proc_counter)
>> +{
>> +       struct cs_proc_state *s;
>> +
>> +       s = queue_find(t->proc_states, match_proc_counter,
>> +                                       UINT_TO_PTR(proc_counter));
>> +       if (s)
>> +               return s;
>> +
>> +       s = new0(struct cs_proc_state, 1);
>> +       s->proc_counter  = proc_counter;
>> +       s->local_status  = CS_PROC_PARTIAL_RESULTS;
>> +       s->remote_status = CS_PROC_PARTIAL_RESULTS;
>> +       /* Copy session-level config from template */
>> +       s->bcs_data.proc_enable_config = t->bcs_proc_data.proc_enable_config;
>> +       s->bcs_data.cs_config = t->bcs_proc_data.cs_config;
>> +       s->bcs_data.t_sw_time_us_supported_by_local =
>> +                       t->bcs_proc_data.t_sw_time_us_supported_by_local;
>> +       s->bcs_data.t_sw_time_us_supported_by_remote =
>> +                       t->bcs_proc_data.t_sw_time_us_supported_by_remote;
>> +       s->bcs_data.ble_conn_interval = t->bcs_proc_data.ble_conn_interval;
>> +       s->bcs_data.initiator_selected_tx_power =
>> +                       t->bcs_proc_data.initiator_selected_tx_power;
>> +       s->bcs_data.procedure_counter = proc_counter;
>> +
>> +       queue_push_tail(t->proc_states, s);
>> +
>> +       return s;
>> +}
>> +
>> +static struct cs_proc_state *find_proc_state_for_ras(struct cstracker *t,
>> +                                               uint16_t ranging_counter)
>> +{
>> +       const struct queue_entry *entry;
>> +
>> +       for (entry = queue_get_entries(t->proc_states); entry;
>> +                                                       entry = entry->next) {
>> +               struct cs_proc_state *s = entry->data;
>> +
>> +               if ((s->proc_counter & CS_RANGING_COUNTER_MASK) ==
>> +                   ranging_counter)
>> +                       return s;
>> +       }
>> +       return NULL;
>> +}
>> +/* ---------- end bcs helpers ---------------------------------------------- */
>> +
>>   static struct ras *rap_get_ras(struct bt_rap *rap)
>>   {
>>          if (!rap)
>> @@ -556,14 +766,6 @@ static struct ras *rap_get_ras(struct bt_rap *rap)
>>          return rap->rrapdb->ras;
>>   }
>>
>> -static void rap_detached(void *data, void *user_data)
>> -{
>> -       struct bt_rap_cb *cb = data;
>> -       struct bt_rap *rap = user_data;
>> -
>> -       cb->detached(rap, cb->user_data);
>> -}
>> -
>>   void bt_rap_detach(struct bt_rap *rap)
>>   {
>>          if (!queue_remove(sessions, rap))
>> @@ -572,8 +774,6 @@ void bt_rap_detach(struct bt_rap *rap)
>>          bt_gatt_client_idle_unregister(rap->client, rap->idle_id);
>>          bt_gatt_client_unref(rap->client);
>>          rap->client = NULL;
>> -
>> -       queue_foreach(bt_rap_cbs, rap_detached, rap);
>>   }
>>
>>   static void rap_db_free(void *data)
>> @@ -608,15 +808,21 @@ static void rap_free(void *data)
>>          rap_db_free(rap->rrapdb);
>>
>>          if (rap->resptracker) {
>> +               free(rap->resptracker->segment_data.iov_base);
>>                  free(rap->resptracker);
>>                  rap->resptracker = NULL;
>>          }
>>
>>          if (rap->reqtracker) {
>> +               queue_destroy(rap->reqtracker->proc_states, free_proc_state);
>> +               free(rap->reqtracker->segment_data.iov_base);
>>                  free(rap->reqtracker);
>>                  rap->reqtracker = NULL;
>>          }
>>
>> +       if (rap->procedure_data_destroy)
>> +               rap->procedure_data_destroy(rap->procedure_data_user_data);
>> +
>>          queue_destroy(rap->notify, free);
>>          queue_destroy(rap->pending, NULL);
>>          queue_destroy(rap->ready_cbs, rap_ready_free);
>> @@ -702,6 +908,24 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
>>          return true;
>>   }
>>
>> +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
>> +                                 bt_rap_procedure_data_func_t cb,
>> +                                 void *user_data,
>> +                                 bt_rap_destroy_func_t destroy)
>> +{
>> +       if (!rap)
>> +               return false;
>> +
>> +       if (rap->procedure_data_destroy)
>> +               rap->procedure_data_destroy(rap->procedure_data_user_data);
>> +
>> +       rap->procedure_data_cb      = cb;
>> +       rap->procedure_data_destroy = destroy;
>> +       rap->procedure_data_user_data = user_data;
>> +
>> +       return true;
>> +}
>> +
>>   static void cs_tracker_init(struct cstracker *t)
>>   {
>>          if (!t)
>> @@ -716,6 +940,7 @@ static void cs_tracker_init(struct cstracker *t)
>>          t->last_start_acl_conn_evt_counter = 0;
>>          t->last_freq_comp = 0;
>>          t->last_ref_pwr_lvl = 0;
>> +       t->proc_states = queue_new();
>>
>>          /* Initialize ranging header using helper functions */
>>          memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
>> @@ -1804,12 +2029,180 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
>>                  cont->step_data);
>>   }
>>
>> +static void write_procedure_data_to_bcs_algo(struct bt_rap *rap,
>> +                                            struct bcs_procedure_data *bcs)
>> +{
>> +       if (!rap || !bcs)
>> +               return;
>> +
>> +       if (rap->reqtracker && rap->reqtracker->proc_start_timestamp_nanos) {
>> +               uint64_t elapsed_nanos = get_time_nanos() -
>> +                               rap->reqtracker->proc_start_timestamp_nanos;
>> +               uint32_t k;
>> +
>> +               DBG(rap, "Procedure elapsed time: %llu nanos",
>> +                   (unsigned long long)elapsed_nanos);
>> +
>> +               for (k = 0; k < bcs->initiator_subevent_count; k++)
>> +                       bcs->initiator_subevent_results[k].timestamp_nanos =
>> +                                                               elapsed_nanos;
>> +               for (k = 0; k < bcs->reflector_subevent_count; k++)
>> +                       bcs->reflector_subevent_results[k].timestamp_nanos =
>> +                                                               elapsed_nanos;
>> +       }
>> +
>> +       DBG(rap, "procedure_counter=%u sequence=%u "
>> +           "init_tx_pwr=%d refl_tx_pwr=%d "
>> +           "init_abort=%d refl_abort=%d "
>> +           "init_subevents=%u refl_subevents=%u",
>> +           bcs->procedure_counter, bcs->procedure_sequence,
>> +           bcs->initiator_selected_tx_power,
>> +           bcs->reflector_selected_tx_power,
>> +           bcs->initiator_procedure_abort_reason,
>> +           bcs->reflector_procedure_abort_reason,
>> +           bcs->initiator_subevent_count,
>> +           bcs->reflector_subevent_count);
>> +
>> +       if (rap->procedure_data_cb)
>> +               rap->procedure_data_cb(rap, bcs,
>> +                                      rap->procedure_data_user_data);
>> +
>> +       bcs_proc_data_clear(bcs);
>> +}
>> +
>> +static void check_cs_procedure_complete(struct bt_rap *rap,
>> +                                        struct cstracker *reqtracker,
>> +                                        struct cs_proc_state *state)
>> +{
>> +       struct bcs_procedure_data *bcs = &state->bcs_data;
>> +
>> +       if (!rap->procedure_data_cb)
>> +               return;
>> +
>> +       if (state->local_status != CS_PROC_ALL_RESULTS_COMPLETE ||
>> +           state->remote_status != CS_PROC_ALL_RESULTS_COMPLETE) {
>> +               DBG(rap, "Procedure not complete: local=%d remote=%d",
>> +                   state->local_status, state->remote_status);
>> +               return;
>> +       }
>> +
>> +       if (!state->local_has_complete_subevent &&
>> +           !state->remote_has_complete_subevent) {
>> +               DBG(rap, "No complete subevent available");
>> +               return;
>> +       }
>> +
>> +       reqtracker->procedure_sequence_after_enable++;
>> +       bcs->procedure_sequence = reqtracker->procedure_sequence_after_enable;
>> +       write_procedure_data_to_bcs_algo(rap, bcs);
>> +
>> +       queue_remove(reqtracker->proc_states, state);
>> +       free(state);
>> +}
>> +
>> +static void parse_cs_local_initiator_data(struct bt_rap *rap,
>> +                                       bool has_header_fields,
>> +                                       uint8_t config_id,
>> +                                       uint8_t num_ant_paths,
>> +                                       uint16_t proc_counter,
>> +                                       uint16_t start_acl_conn_evt_counter,
>> +                                       uint16_t freq_comp,
>> +                                       int8_t ref_pwr_lvl,
>> +                                       uint8_t proc_done_status,
>> +                                       uint8_t subevt_done_status,
>> +                                       uint8_t abort_reason,
>> +                                       uint8_t num_steps_reported,
>> +                                       const struct cs_step_data *hci_steps)
>> +{
>> +       struct cstracker *reqtracker = rap->reqtracker;
>> +       uint16_t effective_counter;
>> +       struct cs_proc_state *state;
>> +       struct bcs_procedure_data *bcs;
>> +
>> +       effective_counter = has_header_fields ? proc_counter
>> +                                             : reqtracker->last_proc_counter;
>> +
>> +       state = find_or_create_proc_state(reqtracker, effective_counter);
>> +       if (!state)
>> +               return;
>> +
>> +       bcs = &state->bcs_data;
>> +
>> +       bcs->initiator_selected_tx_power      = reqtracker->selected_tx_power;
>> +       bcs->initiator_procedure_abort_reason = abort_reason & 0x0F;
>> +
>> +       state->local_status =
>> +               (enum cs_procedure_done_status)(proc_done_status & 0x0F);
>> +       if ((subevt_done_status & 0x0F) == 0x00)
>> +               state->local_has_complete_subevent = true;
>> +
>> +       if (has_header_fields) {
>> +               struct cs_step_data *steps = NULL;
>> +
>> +               reqtracker->proc_start_timestamp_nanos = get_time_nanos();
>> +
>> +               bcs->procedure_counter        = proc_counter;
>> +               reqtracker->last_proc_counter = proc_counter;
>> +               reqtracker->last_start_acl_conn_evt_counter =
>> +                                       start_acl_conn_evt_counter;
>> +               reqtracker->last_freq_comp   = freq_comp;
>> +               reqtracker->last_ref_pwr_lvl = ref_pwr_lvl;
>> +
>> +               if (num_steps_reported > 0 && hci_steps) {
>> +                       steps = calloc(num_steps_reported, sizeof(*steps));
>> +                       if (!steps)
>> +                               return;
>> +                       memcpy(steps, hci_steps,
>> +                              num_steps_reported * sizeof(*steps));
>> +               }
>> +
>> +               bcs_proc_data_add_initiator_subevent(bcs,
>> +                               start_acl_conn_evt_counter,
>> +                               freq_comp, ref_pwr_lvl,
>> +                               num_ant_paths,
>> +                               (abort_reason >> 4) & 0x0F,
>> +                               0,
>> +                               steps, num_steps_reported);
>> +       } else {
>> +               struct cs_subevent_result_data *last;
>> +               struct cs_step_data *new_steps;
>> +               uint32_t old_count, new_count;
>> +
>> +               if (!bcs->initiator_subevent_results ||
>> +                   bcs->initiator_subevent_count == 0)
>> +                       return;
>> +
>> +               last = &bcs->initiator_subevent_results[
>> +                                       bcs->initiator_subevent_count - 1];
>> +               old_count = last->num_steps;
>> +               new_count = old_count + num_steps_reported;
>> +
>> +               if (num_steps_reported > 0 && hci_steps) {
>> +                       new_steps = realloc(last->step_data,
>> +                                           new_count * sizeof(*new_steps));
>> +                       if (!new_steps)
>> +                               return;
>> +
>> +                       memcpy(&new_steps[old_count], hci_steps,
>> +                              num_steps_reported * sizeof(*new_steps));
>> +                       last->step_data = new_steps;
>> +                       last->num_steps = new_count;
>> +               }
>> +
>> +               last->subevent_abort_reason = (abort_reason >> 4) & 0x0F;
>> +       }
>> +
>> +       check_cs_procedure_complete(rap, reqtracker, state);
>> +}
>> +
>> +
>>   static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
>>                  const struct rap_ev_cs_subevent_result_cont *cont,
>>                  uint16_t length)
>>   {
>>          size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
>>                                          step_data);
>> +       struct cstracker *reqtracker;
>>
>>          if (!rap || !rap->reqtracker || !cont)
>>                  return;
>> @@ -1819,6 +2212,22 @@ static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
>>
>>          DBG(rap, "Received CS subevent result continue subevent: len=%u",
>>                  length);
>> +
>> +       reqtracker = rap->reqtracker;
>> +
>> +       parse_cs_local_initiator_data(rap,
>> +                               false,
>> +                               cont->config_id,
>> +                               cont->num_ant_paths,
>> +                               reqtracker->last_proc_counter,
>> +                               reqtracker->last_start_acl_conn_evt_counter,
>> +                               reqtracker->last_freq_comp,
>> +                               reqtracker->last_ref_pwr_lvl,
>> +                               cont->proc_done_status,
>> +                               cont->subevt_done_status,
>> +                               cont->abort_reason,
>> +                               cont->num_steps_reported,
>> +                               cont->step_data);
>>   }
>>
>>   static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
>> @@ -1836,7 +2245,20 @@ static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
>>                  return;
>>
>>          DBG(rap, "Received CS subevent result subevent: len=%u", length);
>> -       /* TODO: Store initiator subevent result data */
>> +
>> +       parse_cs_local_initiator_data(rap,
>> +                               true,
>> +                               data->config_id,
>> +                               data->num_ant_paths,
>> +                               data->proc_counter,
>> +                               data->start_acl_conn_evt_counter,
>> +                               data->freq_comp,
>> +                               data->ref_pwr_lvl,
>> +                               data->proc_done_status,
>> +                               data->subevt_done_status,
>> +                               data->abort_reason,
>> +                               data->num_steps_reported,
>> +                               data->step_data);
>>   }
>>
>>   void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
>> @@ -1880,6 +2302,7 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
>>          const struct rap_ev_cs_proc_enable_cmplt *data = param;
>>          struct bt_rap *rap = user_data;
>>          struct cstracker *resptracker;
>> +       struct cstracker *reqtracker;
>>
>>          DBG(rap, "Received CS procedure enable complete subevent: len=%u",
>>              length);
>> @@ -1895,6 +2318,21 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
>>          /* Populate responder tracker */
>>          resptracker->config_id = data->config_id;
>>          resptracker->selected_tx_power = data->sel_tx_pwr;
>> +
>> +       if (!rap->reqtracker) {
>> +               reqtracker = new0(struct cstracker, 1);
>> +               cs_tracker_init(reqtracker);
>> +               rap->reqtracker = reqtracker;
>> +       }
>> +
>> +       reqtracker = rap->reqtracker;
>> +       reqtracker->config_id = data->config_id;
>> +       reqtracker->selected_tx_power = data->sel_tx_pwr;
>> +       reqtracker->bcs_proc_data.initiator_selected_tx_power =
>> +                                                       data->sel_tx_pwr;
>> +
>> +       /* Store procedure enable config in the session-level template */
>> +       reqtracker->bcs_proc_data.proc_enable_config = *data;
>>   }
>>
>>   void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
>> @@ -1945,6 +2383,39 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
>>          reqtracker->config_id = data->config_id;
>>          reqtracker->role = data->role;
>>          reqtracker->rtt_type = data->rtt_type;
>> +       reqtracker->procedure_sequence_after_enable = 0;
>> +
>> +       /* Store cs_config in session-level template for procedure data */
>> +       reqtracker->bcs_proc_data.cs_config = *data;
>> +       reqtracker->bcs_proc_data.t_sw_time_us_supported_by_local =
>> +                                               rap->local_sw_time;
>> +       reqtracker->bcs_proc_data.t_sw_time_us_supported_by_remote =
>> +                                               rap->remote_sw_time;
>> +       reqtracker->bcs_proc_data.ble_conn_interval = rap->conn_interval;
>> +}
>> +
>> +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time)
>> +{
>> +       if (!rap)
>> +               return;
>> +
>> +       rap->local_sw_time = local_sw_time;
>> +}
>> +
>> +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time)
>> +{
>> +       if (!rap)
>> +               return;
>> +
>> +       rap->remote_sw_time = remote_sw_time;
>> +}
>> +
>> +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval)
>> +{
>> +       if (!rap)
>> +               return;
>> +
>> +       rap->conn_interval = conn_interval;
>>   }
>>
>>   struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
>> @@ -2122,7 +2593,8 @@ static size_t get_mode_zero_length(enum cs_role remote_role)
>>   }
>>
>>   static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
>> -                           enum cs_role remote_role)
>> +                           enum cs_role remote_role,
>> +                           struct cs_mode_zero_data *out)
>>   {
>>          uint8_t packet_quality;
>>          int8_t packet_rssi_dbm;
>> @@ -2143,7 +2615,12 @@ static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
>>                  }
>>          }
>>
>> -       /* TODO: Store this data as reflector data */
>> +       if (out) {
>> +               out->packet_quality = packet_quality;
>> +               out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
>> +               out->packet_ant = packet_ant;
>> +               out->init_measured_freq_offset = init_measured_freq_offset;
>> +       }
>>   }
>>
>>   static size_t get_mode_one_length(bool include_pct)
>> @@ -2154,7 +2631,8 @@ static size_t get_mode_one_length(bool include_pct)
>>   }
>>
>>   static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
>> -                          enum cs_role remote_role, bool include_pct)
>> +                          enum cs_role remote_role, bool include_pct,
>> +                          struct cs_mode_one_data *out)
>>   {
>>          uint8_t packet_quality;
>>          uint8_t packet_nadm;
>> @@ -2178,7 +2656,20 @@ static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
>>                  parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
>>          }
>>
>> -       /* TODO: Store this data as reflector data */
>> +       if (out) {
>> +               out->packet_quality = packet_quality;
>> +               out->packet_nadm = packet_nadm;
>> +               out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
>> +               out->packet_ant = packet_ant;
>> +               if (remote_role == CS_ROLE_REFLECTOR)
>> +                       out->tod_toa_refl = time_value;
>> +               else
>> +                       out->toa_tod_init = time_value;
>> +               out->packet_pct1.i_sample = pct1_i;
>> +               out->packet_pct1.q_sample = pct1_q;
>> +               out->packet_pct2.i_sample = pct2_i;
>> +               out->packet_pct2.q_sample = pct2_q;
>> +       }
>>   }
>>
>>   static size_t get_mode_two_length(uint8_t num_antenna_paths)
>> @@ -2189,7 +2680,8 @@ static size_t get_mode_two_length(uint8_t num_antenna_paths)
>>   }
>>
>>   static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
>> -                          uint8_t num_antenna_paths)
>> +                          uint8_t num_antenna_paths,
>> +                          struct cs_mode_two_data *out)
>>   {
>>          uint8_t ant_perm_index;
>>          int16_t tone_pct_i[5];
>> @@ -2228,7 +2720,14 @@ static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
>>          DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
>>                  ant_perm_index);
>>
>> -       /* TODO: Store this data as reflector data */
>> +       if (out) {
>> +               out->ant_perm_index = ant_perm_index;
>> +               for (k = 0; k < num_paths; k++) {
>> +                       out->tone_pct[k].i_sample = tone_pct_i[k];
>> +                       out->tone_pct[k].q_sample = tone_pct_q[k];
>> +                       out->tone_quality_indicator[k] = tone_quality[k];
>> +               }
>> +       }
>>   }
>>
>>   static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
>> @@ -2239,13 +2738,17 @@ static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
>>
>>   static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
>>                               enum cs_role remote_role, bool include_pct,
>> -                            uint8_t num_antenna_paths)
>> +                            uint8_t num_antenna_paths,
>> +                            struct cs_mode_three_data *out)
>>   {
>> +       struct cs_mode_one_data *out_m1 = out ? &out->mode_one_data : NULL;
>> +       struct cs_mode_two_data *out_m2 = out ? &out->mode_two_data : NULL;
>> +
>>          /* Mode 3 = Mode 1 + Mode 2 */
>> -       parse_mode_one(rap, mode_iov, remote_role, include_pct);
>> +       parse_mode_one(rap, mode_iov, remote_role, include_pct, out_m1);
>>
>>          if (mode_iov->iov_len > 0)
>> -               parse_mode_two(rap, mode_iov, num_antenna_paths);
>> +               parse_mode_two(rap, mode_iov, num_antenna_paths, out_m2);
>>   }
>>
>>   static bool parse_subevent_header(struct iovec *iov,
>> @@ -2278,7 +2781,8 @@ static bool parse_subevent_header(struct iovec *iov,
>>
>>   static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>>                          struct cstracker *reqtracker,
>> -                       uint8_t num_antenna_paths, uint8_t step_idx)
>> +                       uint8_t num_antenna_paths, uint8_t step_idx,
>> +                       struct cs_step_data *out_step)
>>   {
>>          uint8_t mode_byte, step_mode;
>>          bool include_pct;
>> @@ -2337,20 +2841,42 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>>          mode_iov.iov_base = payload;
>>          mode_iov.iov_len = step_payload_len;
>>
>> +       if (out_step) {
>> +               out_step->step_mode = step_mode;
>> +               out_step->step_chnl = 0;
>> +               out_step->step_data_length = (uint8_t)step_payload_len;
>> +       }
>> +
>>          switch (step_mode) {
>> -       case CS_MODE_ZERO:
>> -               parse_mode_zero(rap, &mode_iov, remote_role);
>> +       case CS_MODE_ZERO: {
>> +               struct cs_mode_zero_data *out = out_step ?
>> +                       &out_step->step_mode_data.mode_zero_data : NULL;
>> +
>> +               parse_mode_zero(rap, &mode_iov, remote_role, out);
>>                  break;
>> -       case CS_MODE_ONE:
>> -               parse_mode_one(rap, &mode_iov, remote_role, include_pct);
>> +       }
>> +       case CS_MODE_ONE: {
>> +               struct cs_mode_one_data *out = out_step ?
>> +                       &out_step->step_mode_data.mode_one_data : NULL;
>> +
>> +               parse_mode_one(rap, &mode_iov, remote_role, include_pct, out);
>>                  break;
>> -       case CS_MODE_TWO:
>> -               parse_mode_two(rap, &mode_iov, num_antenna_paths);
>> +       }
>> +       case CS_MODE_TWO: {
>> +               struct cs_mode_two_data *out = out_step ?
>> +                       &out_step->step_mode_data.mode_two_data : NULL;
>> +
>> +               parse_mode_two(rap, &mode_iov, num_antenna_paths, out);
>>                  break;
>> -       case CS_MODE_THREE:
>> +       }
>> +       case CS_MODE_THREE: {
>> +               struct cs_mode_three_data *out = out_step ?
>> +                       &out_step->step_mode_data.mode_three_data : NULL;
>> +
>>                  parse_mode_three(rap, &mode_iov, remote_role, include_pct,
>> -                                               num_antenna_paths);
>> +                                       num_antenna_paths, out);
>>                  break;
>> +       }
>>          default:
>>                  break;
>>          }
>> @@ -2360,12 +2886,16 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>>
>>   static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
>>                                  struct cstracker *reqtracker,
>> -                               uint8_t num_antenna_paths, uint8_t num_steps)
>> +                               uint8_t num_antenna_paths, uint8_t num_steps,
>> +                               struct cs_step_data *out_steps)
>>   {
>>          uint8_t i;
>>
>>          for (i = 0; i < num_steps; i++) {
>> -               if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
>> +               struct cs_step_data *out = out_steps ? &out_steps[i] : NULL;
>> +
>> +               if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i,
>> +                               out))
>>                          break;
>>          }
>>   }
>> @@ -2376,6 +2906,10 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>>          struct iovec iov;
>>          uint8_t antenna_mask;
>>          uint8_t num_antenna_paths;
>> +       uint16_t ranging_counter;
>> +       struct cs_proc_state *state = NULL;
>> +       struct bcs_procedure_data *bcs = NULL;
>> +       bool is_initiator;
>>
>>          if (!rap || !reqtracker)
>>                  return;
>> @@ -2387,10 +2921,30 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>>                  ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
>>          num_antenna_paths = antenna_mask_count_paths(antenna_mask);
>>
>> +       ranging_counter =
>> +               ranging_header_get_counter(&reqtracker->ranging_header_);
>> +
>> +       /* Find the per-procedure state that matches this RAS counter */
>> +       if (rap->procedure_data_cb) {
>> +               state = find_proc_state_for_ras(reqtracker, ranging_counter);
>> +               if (state) {
>> +                       bcs = &state->bcs_data;
>> +                       bcs->reflector_selected_tx_power =
>> +                               reqtracker->ranging_header_.selected_tx_power;
>> +               }
>> +       }
>> +
>> +       /* When local role=INITIATOR, remote data goes to
>> +        * reflector_subevent_results. When local role=REFLECTOR,
>> +        * remote data goes to initiator_subevent_results.
>> +        */
>> +       is_initiator = (reqtracker->role == CS_ROLE_INITIATOR);
>> +
>>          iov = reqtracker->segment_data;
>>
>>          while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
>>                  struct ras_subevent_header hdr;
>> +               struct cs_step_data *steps = NULL;
>>
>>                  if (!parse_subevent_header(&iov, &hdr))
>>                          break;
>> @@ -2402,19 +2956,66 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>>                      hdr.reference_power_level,
>>                      hdr.num_steps_reported);
>>
>> +               if (bcs && hdr.num_steps_reported > 0)
>> +                       steps = calloc(hdr.num_steps_reported, sizeof(*steps));
>> +
>>                  parse_subevent_steps(rap, &iov, reqtracker,
>>                                          num_antenna_paths,
>> -                                       hdr.num_steps_reported);
>> +                                       hdr.num_steps_reported,
>> +                                       steps);
>> +
>> +               if (bcs) {
>> +                       uint32_t stored_steps =
>> +                                       steps ? hdr.num_steps_reported : 0;
>> +
>> +                       if (is_initiator)
>> +                               bcs_proc_data_add_reflector_subevent(bcs,
>> +                                       hdr.start_acl_conn_event,
>> +                                       hdr.frequency_compensation,
>> +                                       hdr.reference_power_level,
>> +                                       num_antenna_paths,
>> +                                       hdr.subevent_abort_reason,
>> +                                       0,
>> +                                       steps, stored_steps);
>> +                       else
>> +                               bcs_proc_data_add_initiator_subevent(bcs,
>> +                                       hdr.start_acl_conn_event,
>> +                                       hdr.frequency_compensation,
>> +                                       hdr.reference_power_level,
>> +                                       num_antenna_paths,
>> +                                       hdr.subevent_abort_reason,
>> +                                       0,
>> +                                       steps, stored_steps);
>> +                       steps = NULL; /* ownership transferred */
>> +               } else {
>> +                       free(steps);
>> +                       steps = NULL;
>> +               }
>>
>> -               if (hdr.subevent_done_status ==
>> -                   SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
>> -                   hdr.ranging_done_status ==
>> +               if (state) {
>> +                       if (hdr.subevent_done_status ==
>> +                           SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
>> +                               state->remote_has_complete_subevent = true;
>> +                       if (hdr.ranging_done_status ==
>> +                           RANGING_DONE_ALL_RESULTS_COMPLETE)
>> +                               state->remote_status =
>> +                                               CS_PROC_ALL_RESULTS_COMPLETE;
>> +               }
>> +
>> +               if (bcs && hdr.ranging_abort_reason)
>> +                       bcs->reflector_procedure_abort_reason =
>> +                                       hdr.ranging_abort_reason & 0x0F;
>> +
>> +               if (hdr.ranging_done_status ==
>>                      RANGING_DONE_ALL_RESULTS_COMPLETE) {
>>                          DBG(rap, "Ranging procedure complete");
>>                          break;
>>                  }
>>          }
>>
>> +       if (state)
>> +               check_cs_procedure_complete(rap, reqtracker, state);
>> +
>>          free(reqtracker->segment_data.iov_base);
>>          reqtracker->segment_data.iov_base = NULL;
>>          reqtracker->segment_data.iov_len = 0;
>> diff --git a/src/shared/rap.h b/src/shared/rap.h
>> index 7bc49f03d..e3f1cf994 100644
>> --- a/src/shared/rap.h
>> +++ b/src/shared/rap.h
>> @@ -157,10 +157,45 @@ struct rap_ev_cs_subevent_result_cont {
>>          struct cs_step_data step_data[];
>>   };
>>
>> +struct cs_subevent_result_data {
>> +       uint16_t             start_acl_conn_evt_counter;
>> +       uint16_t             freq_comp;
>> +       int8_t               ref_pwr_lvl;
>> +       uint8_t              num_ant_paths;
>> +       uint8_t              subevent_abort_reason;
>> +       uint64_t             timestamp_nanos;
>> +       uint32_t             num_steps;
>> +       struct cs_step_data *step_data;
>> +};
>> +
>> +struct bcs_procedure_data {
>> +       uint16_t procedure_counter;
>> +       uint16_t procedure_sequence;
>> +
>> +       int8_t   initiator_selected_tx_power;
>> +       int8_t   reflector_selected_tx_power;
>> +
>> +       struct cs_subevent_result_data *initiator_subevent_results;
>> +       uint32_t initiator_subevent_count;
>> +       uint8_t  initiator_procedure_abort_reason;
>> +
>> +       struct cs_subevent_result_data *reflector_subevent_results;
>> +       uint32_t reflector_subevent_count;
>> +       uint8_t  reflector_procedure_abort_reason;
>> +
>> +       struct rap_ev_cs_proc_enable_cmplt proc_enable_config;
>> +       struct rap_ev_cs_config_cmplt      cs_config;
>> +       uint8_t  t_sw_time_us_supported_by_local;
>> +       uint8_t  t_sw_time_us_supported_by_remote;
>> +       uint16_t ble_conn_interval;
>> +};
>>   typedef void (*bt_rap_debug_func_t)(const char *str, void *user_data);
>>   typedef void (*bt_rap_ready_func_t)(struct bt_rap *rap, void *user_data);
>>   typedef void (*bt_rap_destroy_func_t)(void *user_data);
>>   typedef void (*bt_rap_func_t)(struct bt_rap *rap, void *user_data);
>> +typedef void (*bt_rap_procedure_data_func_t)(struct bt_rap *rap,
>> +                                       struct bcs_procedure_data *data,
>> +                                       void *user_data);
>>
>>   struct bt_rap *bt_rap_ref(struct bt_rap *rap);
>>   void bt_rap_unref(struct bt_rap *rap);
>> @@ -177,6 +212,10 @@ bool bt_rap_set_user_data(struct bt_rap *rap, void *user_data);
>>   bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
>>                          void *user_data, bt_rap_destroy_func_t destroy);
>>
>> +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
>> +                               bt_rap_procedure_data_func_t cb,
>> +                               void *user_data,
>> +                               bt_rap_destroy_func_t destroy);
>>   /* session related functions */
>>   unsigned int bt_rap_register(bt_rap_func_t attached, bt_rap_func_t detached,
>>                                          void *user_data);
>> @@ -211,6 +250,10 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
>>                          int8_t max_tx_power);
>>   void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
>>
>> +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);
>>   /* Connection handle mapping functions */
>>   bool bt_rap_set_conn_hndl(void *hci_sm,
>>                          struct bt_rap *rap,
>> @@ -220,3 +263,7 @@ bool bt_rap_set_conn_hndl(void *hci_sm,
>>                          bool is_central);
>>
>>   void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
>> +/* CS capability sw_time and connection interval setters */
>> +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time);
>> +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time);
>> +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval);
>> --
>> 2.34.1
>>
> 
> 


^ permalink raw reply

* RE: [BlueZ] adapter: Infer BR/EDR only at public LE addresses
From: bluez.test.bot @ 2026-07-16 22:20 UTC (permalink / raw)
  To: linux-bluetooth, pip
In-Reply-To: <20260716204514.79356-1-pip@pipobscure.com>

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

---Test result---

Test Summary:
CheckPatch                    FAIL      0.30 seconds
GitLint                       FAIL      0.22 seconds
BuildEll                      PASS      20.20 seconds
BluezMake                     PASS      550.48 seconds
CheckSmatch                   PASS      308.97 seconds
bluezmakeextell               PASS      100.80 seconds
IncrementalBuild              PASS      542.15 seconds
ScanBuild                     PASS      958.34 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ] adapter: Infer BR/EDR only at public LE addresses
WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#88: 
  bonding_attempt_complete() hci0 bdaddr 5A:B4:98:1F:CC:04 type 0 status 0x4

WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#113: 
Assisted-by: Claude Code:claude-opus-4-8

ERROR:BAD_SIGN_OFF: Unrecognized email address: 'Claude Code:claude-opus-4-8'
#113: 
Assisted-by: Claude Code:claude-opus-4-8

/github/workspace/src/patch/14692154.patch total: 1 errors, 2 warnings, 18 lines checked

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

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

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

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


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ] adapter: Infer BR/EDR only at public LE addresses

7: B3 Line contains hard tab characters (\t): "	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&"
8: B3 Line contains hard tab characters (\t): "				!(eir_data.flags & EIR_BREDR_UNSUP)) {"
9: B3 Line contains hard tab characters (\t): "		device_set_bredr_support(dev);"
15: B3 Line contains hard tab characters (\t): "	if (device->bredr)"
16: B3 Line contains hard tab characters (\t): "		device_browse_sdp(device, NULL);"
17: B3 Line contains hard tab characters (\t): "	else"
18: B3 Line contains hard tab characters (\t): "		device_browse_gatt(device, NULL);"


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [bluez/bluez] b555d9: adapter: Infer BR/EDR only at public LE addresses
From: Philipp Dunkel @ 2026-07-16 21:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1129094
  Home:   https://github.com/bluez/bluez
  Commit: b555d9dbac2ef9a5d1670c08d1600103b211f709
      https://github.com/bluez/bluez/commit/b555d9dbac2ef9a5d1670c08d1600103b211f709
  Author: Philipp Dunkel <pip@pipobscure.com>
  Date:   2026-07-16 (Thu, 16 Jul 2026)

  Changed paths:
    M src/adapter.c

  Log Message:
  -----------
  adapter: Infer BR/EDR only at public LE addresses

btd_adapter_device_found() records BR/EDR support for any LE device
whose advertising Flags lack "BR/EDR Not Supported" (EIR_BREDR_UNSUP).
The address type is not considered:

	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
				!(eir_data.flags & EIR_BREDR_UNSUP)) {
		device_set_bredr_support(dev);

When the advertisement uses a random address the device gets a BR/EDR
bearer whose address is that random address, and start_discovery_cb()
then prefers SDP over GATT:

	if (device->bredr)
		device_browse_sdp(device, NULL);
	else
		device_browse_gatt(device, NULL);

SDP requires a BR/EDR ACL, so bluetoothd pages the random address. A
random address is not a BD_ADDR and cannot be paged, so the attempt
always ends in Page Timeout, and the failure tears down the working LE
link:

  connect_failed_callback() hci0 5A:B4:98:1F:CC:04 status 4
  bonding_attempt_complete() hci0 bdaddr 5A:B4:98:1F:CC:04 type 0 status 0x4
  device_bonding_complete() bonding (nil) status 0x04
  browse_request_cancel()
  btd_gatt_database_att_disconnected()

Nothing requested the connection ("bonding (nil)") and the LE link was
healthy. Any LE association that outlives the page timeout is killed by
it; measured on one host, applications get about 4.7 s from connect
before the link disappears, of which GATT discovery already takes 2.3 s.

Current phones advertise Flags 0x1a (LE General Discoverable plus
Simultaneous LE and BR/EDR, with BR/EDR Not Supported clear) from
resolvable private addresses, so this triggers routinely rather than in
some corner case.

The Flags bits describe what the device supports. They do not promise
that the address it is advertising from is a BD_ADDR. A dual-mode device
uses the same Public Device Address for BR/EDR and LE, so the inference
is sound only for BDADDR_LE_PUBLIC. Restrict it to that.

The other two device_set_bredr_support() callers already require the
device to have been seen over BR/EDR (bdaddr_type == BDADDR_BREDR), so
they are unaffected. A device later seen over BR/EDR, or whose RPA is
resolved to a public identity, still gets BR/EDR support recorded there.

Assisted-by: Claude Code:claude-opus-4-8



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

^ permalink raw reply

* [PATCH BlueZ] adapter: Infer BR/EDR only at public LE addresses
From: Philipp Dunkel @ 2026-07-16 20:45 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Philipp Dunkel

btd_adapter_device_found() records BR/EDR support for any LE device
whose advertising Flags lack "BR/EDR Not Supported" (EIR_BREDR_UNSUP).
The address type is not considered:

	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
				!(eir_data.flags & EIR_BREDR_UNSUP)) {
		device_set_bredr_support(dev);

When the advertisement uses a random address the device gets a BR/EDR
bearer whose address is that random address, and start_discovery_cb()
then prefers SDP over GATT:

	if (device->bredr)
		device_browse_sdp(device, NULL);
	else
		device_browse_gatt(device, NULL);

SDP requires a BR/EDR ACL, so bluetoothd pages the random address. A
random address is not a BD_ADDR and cannot be paged, so the attempt
always ends in Page Timeout, and the failure tears down the working LE
link:

  connect_failed_callback() hci0 5A:B4:98:1F:CC:04 status 4
  bonding_attempt_complete() hci0 bdaddr 5A:B4:98:1F:CC:04 type 0 status 0x4
  device_bonding_complete() bonding (nil) status 0x04
  browse_request_cancel()
  btd_gatt_database_att_disconnected()

Nothing requested the connection ("bonding (nil)") and the LE link was
healthy. Any LE association that outlives the page timeout is killed by
it; measured on one host, applications get about 4.7 s from connect
before the link disappears, of which GATT discovery already takes 2.3 s.

Current phones advertise Flags 0x1a (LE General Discoverable plus
Simultaneous LE and BR/EDR, with BR/EDR Not Supported clear) from
resolvable private addresses, so this triggers routinely rather than in
some corner case.

The Flags bits describe what the device supports. They do not promise
that the address it is advertising from is a BD_ADDR. A dual-mode device
uses the same Public Device Address for BR/EDR and LE, so the inference
is sound only for BDADDR_LE_PUBLIC. Restrict it to that.

The other two device_set_bredr_support() callers already require the
device to have been seen over BR/EDR (bdaddr_type == BDADDR_BREDR), so
they are unaffected. A device later seen over BR/EDR, or whose RPA is
resolved to a public identity, still gets BR/EDR support recorded there.

Assisted-by: Claude Code:claude-opus-4-8
---
Verified on a dual-mode controller with ControllerMode=dual, against iOS and
Android peers advertising Flags 0x1a from resolvable private addresses. A GATT
association that includes a ~7 s user prompt, which previously failed every
time at ~4.7 s with the Page Timeout above, now completes:

  before:  connect +0.0s, GATT resolved +2.3s, first exchange +2.7s,
           Page Timeout +4.7s -> LE link destroyed, transfer never completed
  after:   same host, same peers, classic still enabled, transfer completes
           with the user prompt taking 7.0s of it

Setting ControllerMode=le instead of applying the patch also makes the failure
go away, which independently confirms the mechanism: with no classic radio
there is no page to time out. The patch achieves the same without giving up
BR/EDR, and A2DP/HFP continue to work on the host afterwards.

The patch was reviewed line by line and the result above reproduced on real
hardware by the author, not inferred. make is clean with no new warnings in
src/adapter.c, and make check passes 39/39.

No unit test is included: src/adapter.c is linked only into bluetoothd and not
into any unit test target, so btd_adapter_device_found() cannot be exercised by
the existing harness. Extracting the check into a unit-testable helper looked
like scope creep for a bug fix, but I am happy to add that if preferred.
 src/adapter.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/adapter.c b/src/adapter.c
index 4ffa32a..30e7679 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -7415,8 +7415,17 @@ void btd_adapter_device_found(struct btd_adapter *adapter,
 	 * older kernels send separate adv_ind and scan_rsp. Newer
 	 * kernels send them merged, so once we know which mgmt version
 	 * supports this we can make the non-zero check conditional.
+	 *
+	 * Only infer BR/EDR support when the LE address is a Public Device
+	 * Address. A dual-mode device uses the same Public Device Address for
+	 * BR/EDR and LE, so in that case the advertised address is also a valid
+	 * BD_ADDR to page. A random address (static, resolvable or
+	 * non-resolvable) has no defined relationship to the device's BD_ADDR,
+	 * so recording BR/EDR support against it makes the device look pageable
+	 * at an address that can never be paged: SDP discovery then fails with
+	 * Page Timeout and takes the working LE link down with it.
 	 */
-	if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
+	if (bdaddr_type == BDADDR_LE_PUBLIC && eir_data.flags &&
 					!(eir_data.flags & EIR_BREDR_UNSUP)) {
 		device_set_bredr_support(dev);
 		/* Update last seen for BR/EDR in case its flag is set */
-- 
2.55.0


^ permalink raw reply related

* Re: [PATCH v8 1/9] dt-bindings: mmc: Document fixed-layout NVMEM provider support
From: Rob Herring (Arm) @ 2026-07-16 19:42 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Andrew Lunn, Bjorn Andersson, linux-wireless, Johannes Berg,
	daniel, linux-block, linux-mmc, Paolo Abeni, Eric Dumazet,
	Conor Dooley, netdev, Balakrishna Godavarthi, Simon Horman,
	Srinivas Kandagatla, Luiz Augusto von Dentz, Marcel Holtmann,
	Bartosz Golaszewski, Russell King, Jens Axboe, Konrad Dybcio,
	Jeff Johnson, David S. Miller, Saravana Kannan, linux-bluetooth,
	Rocky Liao, Christian Marangi, Ulf Hansson, Krzysztof Kozlowski,
	Heiner Kallweit, devicetree, linux-arm-msm, linux-kernel, ath10k,
	Jakub Kicinski
In-Reply-To: <20260703-block-as-nvmem-v8-1-98ae32bfc49a@oss.qualcomm.com>


On Fri, 03 Jul 2026 15:45:14 +0200, Loic Poulain wrote:
> Allow an eMMC hardware partition node to describe an NVMEM layout so the
> partition can be exposed as an NVMEM provider. This lets a partition
> (e.g. an eMMC boot partition) store device-specific information such as a
> WiFi MAC address or a Bluetooth BD address and reference it through NVMEM
> cells.
> 
> Accept "fixed-layout" as the partition node compatible, in addition to
> "fixed-partitions", so the layout can be described directly on the
> partition node.
> 
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/mmc/mmc-card.yaml          | 23 +++++++++++++++++++++-
>  1 file changed, 22 insertions(+), 1 deletion(-)
> 

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

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

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

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

^ permalink raw reply

* Re: [PATCH BlueZ v3] shared/rap: Add bcs_procedure_data aggregation and procedure data API
From: Luiz Augusto von Dentz @ 2026-07-16 16:58 UTC (permalink / raw)
  To: Prathibha Madugonde
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260716141706.3640868-1-prathm@qti.qualcomm.com>

Hi Prathibha,

On Thu, Jul 16, 2026 at 10:17 AM Prathibha Madugonde
<prathibha.madugonde@oss.qualcomm.com> wrote:
>
> From: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
>
> Define bcs_procedure_data to hold per-procedure CS results from both
> the local initiator and remote reflector, including subevent step data,
> selected TX powers, procedure enable config, CS config, sw_time values,
> and BLE connection interval.
>
> Add cs_proc_state with a 16-entry ring buffer in cstracker to track
> procedures keyed by procedure counter, accumulating results
> independently from each side until both report all-results-complete.
>
> Update parse_mode_{0,1,2,3}, parse_step, and parse_subevent_steps with
> output pointer parameters to capture remote reflector step data.
> Introduce parse_cs_local_initiator_data() to store local HCI subevent
> results, handling both initial and continuation events. Update
> parse_ras_data_segments() to route decoded RAS subevent data into the
> matching cs_proc_state as reflector results.
>
> Add bt_rap_set_procedure_data_cb() to register a callback fired once
> both local and remote results are complete for a given procedure.
> Add bt_rap_set_local_sw_time(), bt_rap_set_remote_sw_time(), and
> bt_rap_set_conn_interval() so callers such as rap_hci.c can supply the
> CS capability sw_time and connection interval required by upper-layer
> distance calculation algorithms.

Does this mean the spec is incomplete or something? The ACL connection
interval is known, but I have no idea what sw_time would be, is that a
timestamp? And if it is how would that be transported if it is not
part of RAP procedures?

If it is specific to the vendor algorithm it means it doesn't belong
to bt_rap, or RAP does allows to carry vendor specific information?
Even if does then we need to called what it is bt_rap_set_vendor_data,
or something like that, but that would mean the other side needs to
understand this information but I though the distance algorithm is
something only the initiator has to deal with, if both side needs to
negotiate this parameter it means they need to negotiate what
algorithm to use as well.

> Remove unwanted function rap_detached , fix for rap profile level
> disconnections
>
> Changes in V2:
>  Removed the block-scoped variable inside find_or_create_proc_state()
>  Replaced the fixed 16-entry proc_states[] array with a struct queue *
>   of heap-allocated cs_proc_state entries
> ---
>  src/shared/rap.c | 673 ++++++++++++++++++++++++++++++++++++++++++++---
>  src/shared/rap.h |  47 ++++
>  2 files changed, 684 insertions(+), 36 deletions(-)
>
> diff --git a/src/shared/rap.c b/src/shared/rap.c
> index dfb272d3a..eadc41707 100644
> --- a/src/shared/rap.c
> +++ b/src/shared/rap.c
> @@ -12,6 +12,8 @@
>  #include <stdbool.h>
>  #include <unistd.h>
>  #include <errno.h>
> +#include <time.h>
> +#include <glib.h>
>
>  #include "bluetooth/bluetooth.h"
>  #include "bluetooth/hci.h"
> @@ -151,6 +153,16 @@ static inline uint8_t ranging_header_get_antenna_mask(
>         return hdr->antenna_pct & 0x0F;
>  }
>
> +static inline uint16_t ranging_header_get_counter(
> +                                       const struct ranging_header *hdr)
> +{
> +       if (!hdr)
> +               return 0;
> +
> +       return (uint16_t)(hdr->counter_config[0] |
> +                         ((hdr->counter_config[1] & 0x0F) << 8));
> +}
> +
>  static inline uint8_t antenna_mask_count_paths(uint8_t antenna_mask)
>  {
>         uint8_t count = 0;
> @@ -206,11 +218,23 @@ enum cs_role {
>  };
>
>  #define CS_INVALID_CONFIG_ID   0xFF
> +#define CS_RANGING_COUNTER_MASK   0x0FFF
> +
>  /* Minimal enums (align to controller values if needed) */
>  enum cs_procedure_done_status {
>         CS_PROC_ALL_RESULTS_COMPLETE = 0x00,
>         CS_PROC_PARTIAL_RESULTS      = 0x01,
> -       CS_PROC_ABORTED              = 0x02
> +       CS_PROC_ABORTED              = 0x0F
> +};
> +
> +/* Per-procedure tracking — one entry per HCI procedure counter */
> +struct cs_proc_state {
> +       uint16_t proc_counter;
> +       struct bcs_procedure_data bcs_data;
> +       enum cs_procedure_done_status local_status;
> +       bool                          local_has_complete_subevent;
> +       enum cs_procedure_done_status remote_status;
> +       bool                          remote_has_complete_subevent;
>  };
>
>  /* Main cs_procedure_data  */
> @@ -253,6 +277,12 @@ struct cstracker {
>         struct ranging_header   ranging_header_;
>         /* Client - subsequent segments appended using iovec */
>         struct iovec segment_data;
> +
> +       /* Session-level config template and per-procedure state */
> +       struct bcs_procedure_data     bcs_proc_data;
> +       struct queue                  *proc_states;
> +       uint8_t                       procedure_sequence_after_enable;
> +       uint64_t                      proc_start_timestamp_nanos;
>  };
>
>  /* Ranging Service context */
> @@ -300,6 +330,15 @@ struct bt_rap {
>         bt_rap_destroy_func_t debug_destroy;
>         void *debug_data;
>         void *user_data;
> +
> +       bt_rap_procedure_data_func_t  procedure_data_cb;
> +       bt_rap_destroy_func_t         procedure_data_destroy;
> +       void                         *procedure_data_user_data;
> +
> +       /* CS capabilities sw_time and connection interval for config param */
> +       uint8_t local_sw_time;
> +       uint8_t remote_sw_time;
> +       uint16_t conn_interval;
>         struct cstracker *resptracker;
>         struct cstracker *reqtracker;
>  };
> @@ -542,6 +581,177 @@ static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
>         return true;
>  }
>
> +/* ---------- bcs_procedure_data helpers ----------------------------------- */
> +static uint64_t get_time_nanos(void)
> +{
> +       struct timespec ts;
> +
> +       clock_gettime(CLOCK_REALTIME, &ts);
> +       return (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
> +}
> +
> +static void bcs_proc_data_clear(struct bcs_procedure_data *proc)
> +{
> +       uint32_t i;
> +
> +       if (!proc)
> +               return;
> +
> +       for (i = 0; i < proc->initiator_subevent_count; i++)
> +               free(proc->initiator_subevent_results[i].step_data);
> +       free(proc->initiator_subevent_results);
> +       proc->initiator_subevent_results = NULL;
> +       proc->initiator_subevent_count = 0;
> +
> +       for (i = 0; i < proc->reflector_subevent_count; i++)
> +               free(proc->reflector_subevent_results[i].step_data);
> +       free(proc->reflector_subevent_results);
> +       proc->reflector_subevent_results = NULL;
> +       proc->reflector_subevent_count = 0;
> +}
> +
> +static bool bcs_proc_data_add_initiator_subevent(
> +                                       struct bcs_procedure_data *proc,
> +                                       uint16_t start_acl_evt,
> +                                       uint16_t freq_comp,
> +                                       int8_t ref_pwr,
> +                                       uint8_t num_ant_paths,
> +                                       uint8_t abort_reason,
> +                                       uint64_t timestamp_nanos,
> +                                       struct cs_step_data *steps,
> +                                       uint32_t num_steps)
> +{
> +       struct cs_subevent_result_data *arr;
> +       uint32_t new_count;
> +
> +       if (!proc)
> +               return false;
> +
> +       new_count = proc->initiator_subevent_count + 1;
> +       arr = realloc(proc->initiator_subevent_results,
> +                                       new_count * sizeof(*arr));
> +       if (!arr) {
> +               free(steps);
> +               return false;
> +       }
> +
> +       arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
> +       arr[new_count - 1].freq_comp = freq_comp;
> +       arr[new_count - 1].ref_pwr_lvl = ref_pwr;
> +       arr[new_count - 1].num_ant_paths = num_ant_paths;
> +       arr[new_count - 1].subevent_abort_reason = abort_reason;
> +       arr[new_count - 1].timestamp_nanos = timestamp_nanos;
> +       arr[new_count - 1].num_steps = num_steps;
> +       arr[new_count - 1].step_data = steps;
> +
> +       proc->initiator_subevent_results = arr;
> +       proc->initiator_subevent_count = new_count;
> +       return true;
> +}
> +
> +static bool bcs_proc_data_add_reflector_subevent(
> +                                       struct bcs_procedure_data *proc,
> +                                       uint16_t start_acl_evt,
> +                                       uint16_t freq_comp,
> +                                       int8_t ref_pwr,
> +                                       uint8_t num_ant_paths,
> +                                       uint8_t abort_reason,
> +                                       uint64_t timestamp_nanos,
> +                                       struct cs_step_data *steps,
> +                                       uint32_t num_steps)
> +{
> +       struct cs_subevent_result_data *arr;
> +       uint32_t new_count;
> +
> +       if (!proc)
> +               return false;
> +
> +       new_count = proc->reflector_subevent_count + 1;
> +       arr = realloc(proc->reflector_subevent_results,
> +                                       new_count * sizeof(*arr));
> +       if (!arr) {
> +               free(steps);
> +               return false;
> +       }
> +
> +       arr[new_count - 1].start_acl_conn_evt_counter = start_acl_evt;
> +       arr[new_count - 1].freq_comp = freq_comp;
> +       arr[new_count - 1].ref_pwr_lvl = ref_pwr;
> +       arr[new_count - 1].num_ant_paths = num_ant_paths;
> +       arr[new_count - 1].subevent_abort_reason = abort_reason;
> +       arr[new_count - 1].timestamp_nanos = timestamp_nanos;
> +       arr[new_count - 1].num_steps = num_steps;
> +       arr[new_count - 1].step_data = steps;
> +
> +       proc->reflector_subevent_results = arr;
> +       proc->reflector_subevent_count = new_count;
> +       return true;
> +}
> +
> +static bool match_proc_counter(const void *data, const void *match_data)
> +{
> +       const struct cs_proc_state *s = data;
> +       uint16_t proc_counter = PTR_TO_UINT(match_data);
> +
> +       return s->proc_counter == proc_counter;
> +}
> +
> +static void free_proc_state(void *data)
> +{
> +       struct cs_proc_state *s = data;
> +
> +       bcs_proc_data_clear(&s->bcs_data);
> +       free(s);
> +}
> +
> +static struct cs_proc_state *find_or_create_proc_state(struct cstracker *t,
> +                                                       uint16_t proc_counter)
> +{
> +       struct cs_proc_state *s;
> +
> +       s = queue_find(t->proc_states, match_proc_counter,
> +                                       UINT_TO_PTR(proc_counter));
> +       if (s)
> +               return s;
> +
> +       s = new0(struct cs_proc_state, 1);
> +       s->proc_counter  = proc_counter;
> +       s->local_status  = CS_PROC_PARTIAL_RESULTS;
> +       s->remote_status = CS_PROC_PARTIAL_RESULTS;
> +       /* Copy session-level config from template */
> +       s->bcs_data.proc_enable_config = t->bcs_proc_data.proc_enable_config;
> +       s->bcs_data.cs_config = t->bcs_proc_data.cs_config;
> +       s->bcs_data.t_sw_time_us_supported_by_local =
> +                       t->bcs_proc_data.t_sw_time_us_supported_by_local;
> +       s->bcs_data.t_sw_time_us_supported_by_remote =
> +                       t->bcs_proc_data.t_sw_time_us_supported_by_remote;
> +       s->bcs_data.ble_conn_interval = t->bcs_proc_data.ble_conn_interval;
> +       s->bcs_data.initiator_selected_tx_power =
> +                       t->bcs_proc_data.initiator_selected_tx_power;
> +       s->bcs_data.procedure_counter = proc_counter;
> +
> +       queue_push_tail(t->proc_states, s);
> +
> +       return s;
> +}
> +
> +static struct cs_proc_state *find_proc_state_for_ras(struct cstracker *t,
> +                                               uint16_t ranging_counter)
> +{
> +       const struct queue_entry *entry;
> +
> +       for (entry = queue_get_entries(t->proc_states); entry;
> +                                                       entry = entry->next) {
> +               struct cs_proc_state *s = entry->data;
> +
> +               if ((s->proc_counter & CS_RANGING_COUNTER_MASK) ==
> +                   ranging_counter)
> +                       return s;
> +       }
> +       return NULL;
> +}
> +/* ---------- end bcs helpers ---------------------------------------------- */
> +
>  static struct ras *rap_get_ras(struct bt_rap *rap)
>  {
>         if (!rap)
> @@ -556,14 +766,6 @@ static struct ras *rap_get_ras(struct bt_rap *rap)
>         return rap->rrapdb->ras;
>  }
>
> -static void rap_detached(void *data, void *user_data)
> -{
> -       struct bt_rap_cb *cb = data;
> -       struct bt_rap *rap = user_data;
> -
> -       cb->detached(rap, cb->user_data);
> -}
> -
>  void bt_rap_detach(struct bt_rap *rap)
>  {
>         if (!queue_remove(sessions, rap))
> @@ -572,8 +774,6 @@ void bt_rap_detach(struct bt_rap *rap)
>         bt_gatt_client_idle_unregister(rap->client, rap->idle_id);
>         bt_gatt_client_unref(rap->client);
>         rap->client = NULL;
> -
> -       queue_foreach(bt_rap_cbs, rap_detached, rap);
>  }
>
>  static void rap_db_free(void *data)
> @@ -608,15 +808,21 @@ static void rap_free(void *data)
>         rap_db_free(rap->rrapdb);
>
>         if (rap->resptracker) {
> +               free(rap->resptracker->segment_data.iov_base);
>                 free(rap->resptracker);
>                 rap->resptracker = NULL;
>         }
>
>         if (rap->reqtracker) {
> +               queue_destroy(rap->reqtracker->proc_states, free_proc_state);
> +               free(rap->reqtracker->segment_data.iov_base);
>                 free(rap->reqtracker);
>                 rap->reqtracker = NULL;
>         }
>
> +       if (rap->procedure_data_destroy)
> +               rap->procedure_data_destroy(rap->procedure_data_user_data);
> +
>         queue_destroy(rap->notify, free);
>         queue_destroy(rap->pending, NULL);
>         queue_destroy(rap->ready_cbs, rap_ready_free);
> @@ -702,6 +908,24 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
>         return true;
>  }
>
> +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
> +                                 bt_rap_procedure_data_func_t cb,
> +                                 void *user_data,
> +                                 bt_rap_destroy_func_t destroy)
> +{
> +       if (!rap)
> +               return false;
> +
> +       if (rap->procedure_data_destroy)
> +               rap->procedure_data_destroy(rap->procedure_data_user_data);
> +
> +       rap->procedure_data_cb      = cb;
> +       rap->procedure_data_destroy = destroy;
> +       rap->procedure_data_user_data = user_data;
> +
> +       return true;
> +}
> +
>  static void cs_tracker_init(struct cstracker *t)
>  {
>         if (!t)
> @@ -716,6 +940,7 @@ static void cs_tracker_init(struct cstracker *t)
>         t->last_start_acl_conn_evt_counter = 0;
>         t->last_freq_comp = 0;
>         t->last_ref_pwr_lvl = 0;
> +       t->proc_states = queue_new();
>
>         /* Initialize ranging header using helper functions */
>         memset(&t->ranging_header_, 0, sizeof(t->ranging_header_));
> @@ -1804,12 +2029,180 @@ static void form_ras_data_with_cs_subevent_result_cont(struct bt_rap *rap,
>                 cont->step_data);
>  }
>
> +static void write_procedure_data_to_bcs_algo(struct bt_rap *rap,
> +                                            struct bcs_procedure_data *bcs)
> +{
> +       if (!rap || !bcs)
> +               return;
> +
> +       if (rap->reqtracker && rap->reqtracker->proc_start_timestamp_nanos) {
> +               uint64_t elapsed_nanos = get_time_nanos() -
> +                               rap->reqtracker->proc_start_timestamp_nanos;
> +               uint32_t k;
> +
> +               DBG(rap, "Procedure elapsed time: %llu nanos",
> +                   (unsigned long long)elapsed_nanos);
> +
> +               for (k = 0; k < bcs->initiator_subevent_count; k++)
> +                       bcs->initiator_subevent_results[k].timestamp_nanos =
> +                                                               elapsed_nanos;
> +               for (k = 0; k < bcs->reflector_subevent_count; k++)
> +                       bcs->reflector_subevent_results[k].timestamp_nanos =
> +                                                               elapsed_nanos;
> +       }
> +
> +       DBG(rap, "procedure_counter=%u sequence=%u "
> +           "init_tx_pwr=%d refl_tx_pwr=%d "
> +           "init_abort=%d refl_abort=%d "
> +           "init_subevents=%u refl_subevents=%u",
> +           bcs->procedure_counter, bcs->procedure_sequence,
> +           bcs->initiator_selected_tx_power,
> +           bcs->reflector_selected_tx_power,
> +           bcs->initiator_procedure_abort_reason,
> +           bcs->reflector_procedure_abort_reason,
> +           bcs->initiator_subevent_count,
> +           bcs->reflector_subevent_count);
> +
> +       if (rap->procedure_data_cb)
> +               rap->procedure_data_cb(rap, bcs,
> +                                      rap->procedure_data_user_data);
> +
> +       bcs_proc_data_clear(bcs);
> +}
> +
> +static void check_cs_procedure_complete(struct bt_rap *rap,
> +                                        struct cstracker *reqtracker,
> +                                        struct cs_proc_state *state)
> +{
> +       struct bcs_procedure_data *bcs = &state->bcs_data;
> +
> +       if (!rap->procedure_data_cb)
> +               return;
> +
> +       if (state->local_status != CS_PROC_ALL_RESULTS_COMPLETE ||
> +           state->remote_status != CS_PROC_ALL_RESULTS_COMPLETE) {
> +               DBG(rap, "Procedure not complete: local=%d remote=%d",
> +                   state->local_status, state->remote_status);
> +               return;
> +       }
> +
> +       if (!state->local_has_complete_subevent &&
> +           !state->remote_has_complete_subevent) {
> +               DBG(rap, "No complete subevent available");
> +               return;
> +       }
> +
> +       reqtracker->procedure_sequence_after_enable++;
> +       bcs->procedure_sequence = reqtracker->procedure_sequence_after_enable;
> +       write_procedure_data_to_bcs_algo(rap, bcs);
> +
> +       queue_remove(reqtracker->proc_states, state);
> +       free(state);
> +}
> +
> +static void parse_cs_local_initiator_data(struct bt_rap *rap,
> +                                       bool has_header_fields,
> +                                       uint8_t config_id,
> +                                       uint8_t num_ant_paths,
> +                                       uint16_t proc_counter,
> +                                       uint16_t start_acl_conn_evt_counter,
> +                                       uint16_t freq_comp,
> +                                       int8_t ref_pwr_lvl,
> +                                       uint8_t proc_done_status,
> +                                       uint8_t subevt_done_status,
> +                                       uint8_t abort_reason,
> +                                       uint8_t num_steps_reported,
> +                                       const struct cs_step_data *hci_steps)
> +{
> +       struct cstracker *reqtracker = rap->reqtracker;
> +       uint16_t effective_counter;
> +       struct cs_proc_state *state;
> +       struct bcs_procedure_data *bcs;
> +
> +       effective_counter = has_header_fields ? proc_counter
> +                                             : reqtracker->last_proc_counter;
> +
> +       state = find_or_create_proc_state(reqtracker, effective_counter);
> +       if (!state)
> +               return;
> +
> +       bcs = &state->bcs_data;
> +
> +       bcs->initiator_selected_tx_power      = reqtracker->selected_tx_power;
> +       bcs->initiator_procedure_abort_reason = abort_reason & 0x0F;
> +
> +       state->local_status =
> +               (enum cs_procedure_done_status)(proc_done_status & 0x0F);
> +       if ((subevt_done_status & 0x0F) == 0x00)
> +               state->local_has_complete_subevent = true;
> +
> +       if (has_header_fields) {
> +               struct cs_step_data *steps = NULL;
> +
> +               reqtracker->proc_start_timestamp_nanos = get_time_nanos();
> +
> +               bcs->procedure_counter        = proc_counter;
> +               reqtracker->last_proc_counter = proc_counter;
> +               reqtracker->last_start_acl_conn_evt_counter =
> +                                       start_acl_conn_evt_counter;
> +               reqtracker->last_freq_comp   = freq_comp;
> +               reqtracker->last_ref_pwr_lvl = ref_pwr_lvl;
> +
> +               if (num_steps_reported > 0 && hci_steps) {
> +                       steps = calloc(num_steps_reported, sizeof(*steps));
> +                       if (!steps)
> +                               return;
> +                       memcpy(steps, hci_steps,
> +                              num_steps_reported * sizeof(*steps));
> +               }
> +
> +               bcs_proc_data_add_initiator_subevent(bcs,
> +                               start_acl_conn_evt_counter,
> +                               freq_comp, ref_pwr_lvl,
> +                               num_ant_paths,
> +                               (abort_reason >> 4) & 0x0F,
> +                               0,
> +                               steps, num_steps_reported);
> +       } else {
> +               struct cs_subevent_result_data *last;
> +               struct cs_step_data *new_steps;
> +               uint32_t old_count, new_count;
> +
> +               if (!bcs->initiator_subevent_results ||
> +                   bcs->initiator_subevent_count == 0)
> +                       return;
> +
> +               last = &bcs->initiator_subevent_results[
> +                                       bcs->initiator_subevent_count - 1];
> +               old_count = last->num_steps;
> +               new_count = old_count + num_steps_reported;
> +
> +               if (num_steps_reported > 0 && hci_steps) {
> +                       new_steps = realloc(last->step_data,
> +                                           new_count * sizeof(*new_steps));
> +                       if (!new_steps)
> +                               return;
> +
> +                       memcpy(&new_steps[old_count], hci_steps,
> +                              num_steps_reported * sizeof(*new_steps));
> +                       last->step_data = new_steps;
> +                       last->num_steps = new_count;
> +               }
> +
> +               last->subevent_abort_reason = (abort_reason >> 4) & 0x0F;
> +       }
> +
> +       check_cs_procedure_complete(rap, reqtracker, state);
> +}
> +
> +
>  static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
>                 const struct rap_ev_cs_subevent_result_cont *cont,
>                 uint16_t length)
>  {
>         size_t base_len = offsetof(struct rap_ev_cs_subevent_result_cont,
>                                         step_data);
> +       struct cstracker *reqtracker;
>
>         if (!rap || !rap->reqtracker || !cont)
>                 return;
> @@ -1819,6 +2212,22 @@ static void fill_initiator_data_from_cs_subevent_result_cont(struct bt_rap *rap,
>
>         DBG(rap, "Received CS subevent result continue subevent: len=%u",
>                 length);
> +
> +       reqtracker = rap->reqtracker;
> +
> +       parse_cs_local_initiator_data(rap,
> +                               false,
> +                               cont->config_id,
> +                               cont->num_ant_paths,
> +                               reqtracker->last_proc_counter,
> +                               reqtracker->last_start_acl_conn_evt_counter,
> +                               reqtracker->last_freq_comp,
> +                               reqtracker->last_ref_pwr_lvl,
> +                               cont->proc_done_status,
> +                               cont->subevt_done_status,
> +                               cont->abort_reason,
> +                               cont->num_steps_reported,
> +                               cont->step_data);
>  }
>
>  static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
> @@ -1836,7 +2245,20 @@ static void fill_initiator_data_from_cs_subevent_result(struct bt_rap *rap,
>                 return;
>
>         DBG(rap, "Received CS subevent result subevent: len=%u", length);
> -       /* TODO: Store initiator subevent result data */
> +
> +       parse_cs_local_initiator_data(rap,
> +                               true,
> +                               data->config_id,
> +                               data->num_ant_paths,
> +                               data->proc_counter,
> +                               data->start_acl_conn_evt_counter,
> +                               data->freq_comp,
> +                               data->ref_pwr_lvl,
> +                               data->proc_done_status,
> +                               data->subevt_done_status,
> +                               data->abort_reason,
> +                               data->num_steps_reported,
> +                               data->step_data);
>  }
>
>  void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
> @@ -1880,6 +2302,7 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
>         const struct rap_ev_cs_proc_enable_cmplt *data = param;
>         struct bt_rap *rap = user_data;
>         struct cstracker *resptracker;
> +       struct cstracker *reqtracker;
>
>         DBG(rap, "Received CS procedure enable complete subevent: len=%u",
>             length);
> @@ -1895,6 +2318,21 @@ void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
>         /* Populate responder tracker */
>         resptracker->config_id = data->config_id;
>         resptracker->selected_tx_power = data->sel_tx_pwr;
> +
> +       if (!rap->reqtracker) {
> +               reqtracker = new0(struct cstracker, 1);
> +               cs_tracker_init(reqtracker);
> +               rap->reqtracker = reqtracker;
> +       }
> +
> +       reqtracker = rap->reqtracker;
> +       reqtracker->config_id = data->config_id;
> +       reqtracker->selected_tx_power = data->sel_tx_pwr;
> +       reqtracker->bcs_proc_data.initiator_selected_tx_power =
> +                                                       data->sel_tx_pwr;
> +
> +       /* Store procedure enable config in the session-level template */
> +       reqtracker->bcs_proc_data.proc_enable_config = *data;
>  }
>
>  void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
> @@ -1945,6 +2383,39 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
>         reqtracker->config_id = data->config_id;
>         reqtracker->role = data->role;
>         reqtracker->rtt_type = data->rtt_type;
> +       reqtracker->procedure_sequence_after_enable = 0;
> +
> +       /* Store cs_config in session-level template for procedure data */
> +       reqtracker->bcs_proc_data.cs_config = *data;
> +       reqtracker->bcs_proc_data.t_sw_time_us_supported_by_local =
> +                                               rap->local_sw_time;
> +       reqtracker->bcs_proc_data.t_sw_time_us_supported_by_remote =
> +                                               rap->remote_sw_time;
> +       reqtracker->bcs_proc_data.ble_conn_interval = rap->conn_interval;
> +}
> +
> +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time)
> +{
> +       if (!rap)
> +               return;
> +
> +       rap->local_sw_time = local_sw_time;
> +}
> +
> +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time)
> +{
> +       if (!rap)
> +               return;
> +
> +       rap->remote_sw_time = remote_sw_time;
> +}
> +
> +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval)
> +{
> +       if (!rap)
> +               return;
> +
> +       rap->conn_interval = conn_interval;
>  }
>
>  struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
> @@ -2122,7 +2593,8 @@ static size_t get_mode_zero_length(enum cs_role remote_role)
>  }
>
>  static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
> -                           enum cs_role remote_role)
> +                           enum cs_role remote_role,
> +                           struct cs_mode_zero_data *out)
>  {
>         uint8_t packet_quality;
>         int8_t packet_rssi_dbm;
> @@ -2143,7 +2615,12 @@ static void parse_mode_zero(struct bt_rap *rap, struct iovec *mode_iov,
>                 }
>         }
>
> -       /* TODO: Store this data as reflector data */
> +       if (out) {
> +               out->packet_quality = packet_quality;
> +               out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
> +               out->packet_ant = packet_ant;
> +               out->init_measured_freq_offset = init_measured_freq_offset;
> +       }
>  }
>
>  static size_t get_mode_one_length(bool include_pct)
> @@ -2154,7 +2631,8 @@ static size_t get_mode_one_length(bool include_pct)
>  }
>
>  static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
> -                          enum cs_role remote_role, bool include_pct)
> +                          enum cs_role remote_role, bool include_pct,
> +                          struct cs_mode_one_data *out)
>  {
>         uint8_t packet_quality;
>         uint8_t packet_nadm;
> @@ -2178,7 +2656,20 @@ static void parse_mode_one(struct bt_rap *rap, struct iovec *mode_iov,
>                 parse_i_q_sample(mode_iov, &pct2_i, &pct2_q);
>         }
>
> -       /* TODO: Store this data as reflector data */
> +       if (out) {
> +               out->packet_quality = packet_quality;
> +               out->packet_nadm = packet_nadm;
> +               out->packet_rssi_dbm = (uint8_t)packet_rssi_dbm;
> +               out->packet_ant = packet_ant;
> +               if (remote_role == CS_ROLE_REFLECTOR)
> +                       out->tod_toa_refl = time_value;
> +               else
> +                       out->toa_tod_init = time_value;
> +               out->packet_pct1.i_sample = pct1_i;
> +               out->packet_pct1.q_sample = pct1_q;
> +               out->packet_pct2.i_sample = pct2_i;
> +               out->packet_pct2.q_sample = pct2_q;
> +       }
>  }
>
>  static size_t get_mode_two_length(uint8_t num_antenna_paths)
> @@ -2189,7 +2680,8 @@ static size_t get_mode_two_length(uint8_t num_antenna_paths)
>  }
>
>  static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
> -                          uint8_t num_antenna_paths)
> +                          uint8_t num_antenna_paths,
> +                          struct cs_mode_two_data *out)
>  {
>         uint8_t ant_perm_index;
>         int16_t tone_pct_i[5];
> @@ -2228,7 +2720,14 @@ static void parse_mode_two(struct bt_rap *rap, struct iovec *mode_iov,
>         DBG(rap, "    cs_mode_two_data: ant_perm_idx=%u",
>                 ant_perm_index);
>
> -       /* TODO: Store this data as reflector data */
> +       if (out) {
> +               out->ant_perm_index = ant_perm_index;
> +               for (k = 0; k < num_paths; k++) {
> +                       out->tone_pct[k].i_sample = tone_pct_i[k];
> +                       out->tone_pct[k].q_sample = tone_pct_q[k];
> +                       out->tone_quality_indicator[k] = tone_quality[k];
> +               }
> +       }
>  }
>
>  static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
> @@ -2239,13 +2738,17 @@ static size_t get_mode_three_length(uint8_t num_antenna_paths, bool include_pct)
>
>  static void parse_mode_three(struct bt_rap *rap, struct iovec *mode_iov,
>                              enum cs_role remote_role, bool include_pct,
> -                            uint8_t num_antenna_paths)
> +                            uint8_t num_antenna_paths,
> +                            struct cs_mode_three_data *out)
>  {
> +       struct cs_mode_one_data *out_m1 = out ? &out->mode_one_data : NULL;
> +       struct cs_mode_two_data *out_m2 = out ? &out->mode_two_data : NULL;
> +
>         /* Mode 3 = Mode 1 + Mode 2 */
> -       parse_mode_one(rap, mode_iov, remote_role, include_pct);
> +       parse_mode_one(rap, mode_iov, remote_role, include_pct, out_m1);
>
>         if (mode_iov->iov_len > 0)
> -               parse_mode_two(rap, mode_iov, num_antenna_paths);
> +               parse_mode_two(rap, mode_iov, num_antenna_paths, out_m2);
>  }
>
>  static bool parse_subevent_header(struct iovec *iov,
> @@ -2278,7 +2781,8 @@ static bool parse_subevent_header(struct iovec *iov,
>
>  static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>                         struct cstracker *reqtracker,
> -                       uint8_t num_antenna_paths, uint8_t step_idx)
> +                       uint8_t num_antenna_paths, uint8_t step_idx,
> +                       struct cs_step_data *out_step)
>  {
>         uint8_t mode_byte, step_mode;
>         bool include_pct;
> @@ -2337,20 +2841,42 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>         mode_iov.iov_base = payload;
>         mode_iov.iov_len = step_payload_len;
>
> +       if (out_step) {
> +               out_step->step_mode = step_mode;
> +               out_step->step_chnl = 0;
> +               out_step->step_data_length = (uint8_t)step_payload_len;
> +       }
> +
>         switch (step_mode) {
> -       case CS_MODE_ZERO:
> -               parse_mode_zero(rap, &mode_iov, remote_role);
> +       case CS_MODE_ZERO: {
> +               struct cs_mode_zero_data *out = out_step ?
> +                       &out_step->step_mode_data.mode_zero_data : NULL;
> +
> +               parse_mode_zero(rap, &mode_iov, remote_role, out);
>                 break;
> -       case CS_MODE_ONE:
> -               parse_mode_one(rap, &mode_iov, remote_role, include_pct);
> +       }
> +       case CS_MODE_ONE: {
> +               struct cs_mode_one_data *out = out_step ?
> +                       &out_step->step_mode_data.mode_one_data : NULL;
> +
> +               parse_mode_one(rap, &mode_iov, remote_role, include_pct, out);
>                 break;
> -       case CS_MODE_TWO:
> -               parse_mode_two(rap, &mode_iov, num_antenna_paths);
> +       }
> +       case CS_MODE_TWO: {
> +               struct cs_mode_two_data *out = out_step ?
> +                       &out_step->step_mode_data.mode_two_data : NULL;
> +
> +               parse_mode_two(rap, &mode_iov, num_antenna_paths, out);
>                 break;
> -       case CS_MODE_THREE:
> +       }
> +       case CS_MODE_THREE: {
> +               struct cs_mode_three_data *out = out_step ?
> +                       &out_step->step_mode_data.mode_three_data : NULL;
> +
>                 parse_mode_three(rap, &mode_iov, remote_role, include_pct,
> -                                               num_antenna_paths);
> +                                       num_antenna_paths, out);
>                 break;
> +       }
>         default:
>                 break;
>         }
> @@ -2360,12 +2886,16 @@ static bool parse_step(struct bt_rap *rap, struct iovec *iov,
>
>  static void parse_subevent_steps(struct bt_rap *rap, struct iovec *iov,
>                                 struct cstracker *reqtracker,
> -                               uint8_t num_antenna_paths, uint8_t num_steps)
> +                               uint8_t num_antenna_paths, uint8_t num_steps,
> +                               struct cs_step_data *out_steps)
>  {
>         uint8_t i;
>
>         for (i = 0; i < num_steps; i++) {
> -               if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i))
> +               struct cs_step_data *out = out_steps ? &out_steps[i] : NULL;
> +
> +               if (!parse_step(rap, iov, reqtracker, num_antenna_paths, i,
> +                               out))
>                         break;
>         }
>  }
> @@ -2376,6 +2906,10 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>         struct iovec iov;
>         uint8_t antenna_mask;
>         uint8_t num_antenna_paths;
> +       uint16_t ranging_counter;
> +       struct cs_proc_state *state = NULL;
> +       struct bcs_procedure_data *bcs = NULL;
> +       bool is_initiator;
>
>         if (!rap || !reqtracker)
>                 return;
> @@ -2387,10 +2921,30 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>                 ranging_header_get_antenna_mask(&reqtracker->ranging_header_);
>         num_antenna_paths = antenna_mask_count_paths(antenna_mask);
>
> +       ranging_counter =
> +               ranging_header_get_counter(&reqtracker->ranging_header_);
> +
> +       /* Find the per-procedure state that matches this RAS counter */
> +       if (rap->procedure_data_cb) {
> +               state = find_proc_state_for_ras(reqtracker, ranging_counter);
> +               if (state) {
> +                       bcs = &state->bcs_data;
> +                       bcs->reflector_selected_tx_power =
> +                               reqtracker->ranging_header_.selected_tx_power;
> +               }
> +       }
> +
> +       /* When local role=INITIATOR, remote data goes to
> +        * reflector_subevent_results. When local role=REFLECTOR,
> +        * remote data goes to initiator_subevent_results.
> +        */
> +       is_initiator = (reqtracker->role == CS_ROLE_INITIATOR);
> +
>         iov = reqtracker->segment_data;
>
>         while (iov.iov_len >= RAS_SUBEVENT_HEADER_SIZE) {
>                 struct ras_subevent_header hdr;
> +               struct cs_step_data *steps = NULL;
>
>                 if (!parse_subevent_header(&iov, &hdr))
>                         break;
> @@ -2402,19 +2956,66 @@ static void parse_ras_data_segments(struct bt_rap *rap,
>                     hdr.reference_power_level,
>                     hdr.num_steps_reported);
>
> +               if (bcs && hdr.num_steps_reported > 0)
> +                       steps = calloc(hdr.num_steps_reported, sizeof(*steps));
> +
>                 parse_subevent_steps(rap, &iov, reqtracker,
>                                         num_antenna_paths,
> -                                       hdr.num_steps_reported);
> +                                       hdr.num_steps_reported,
> +                                       steps);
> +
> +               if (bcs) {
> +                       uint32_t stored_steps =
> +                                       steps ? hdr.num_steps_reported : 0;
> +
> +                       if (is_initiator)
> +                               bcs_proc_data_add_reflector_subevent(bcs,
> +                                       hdr.start_acl_conn_event,
> +                                       hdr.frequency_compensation,
> +                                       hdr.reference_power_level,
> +                                       num_antenna_paths,
> +                                       hdr.subevent_abort_reason,
> +                                       0,
> +                                       steps, stored_steps);
> +                       else
> +                               bcs_proc_data_add_initiator_subevent(bcs,
> +                                       hdr.start_acl_conn_event,
> +                                       hdr.frequency_compensation,
> +                                       hdr.reference_power_level,
> +                                       num_antenna_paths,
> +                                       hdr.subevent_abort_reason,
> +                                       0,
> +                                       steps, stored_steps);
> +                       steps = NULL; /* ownership transferred */
> +               } else {
> +                       free(steps);
> +                       steps = NULL;
> +               }
>
> -               if (hdr.subevent_done_status ==
> -                   SUBEVENT_DONE_ALL_RESULTS_COMPLETE ||
> -                   hdr.ranging_done_status ==
> +               if (state) {
> +                       if (hdr.subevent_done_status ==
> +                           SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
> +                               state->remote_has_complete_subevent = true;
> +                       if (hdr.ranging_done_status ==
> +                           RANGING_DONE_ALL_RESULTS_COMPLETE)
> +                               state->remote_status =
> +                                               CS_PROC_ALL_RESULTS_COMPLETE;
> +               }
> +
> +               if (bcs && hdr.ranging_abort_reason)
> +                       bcs->reflector_procedure_abort_reason =
> +                                       hdr.ranging_abort_reason & 0x0F;
> +
> +               if (hdr.ranging_done_status ==
>                     RANGING_DONE_ALL_RESULTS_COMPLETE) {
>                         DBG(rap, "Ranging procedure complete");
>                         break;
>                 }
>         }
>
> +       if (state)
> +               check_cs_procedure_complete(rap, reqtracker, state);
> +
>         free(reqtracker->segment_data.iov_base);
>         reqtracker->segment_data.iov_base = NULL;
>         reqtracker->segment_data.iov_len = 0;
> diff --git a/src/shared/rap.h b/src/shared/rap.h
> index 7bc49f03d..e3f1cf994 100644
> --- a/src/shared/rap.h
> +++ b/src/shared/rap.h
> @@ -157,10 +157,45 @@ struct rap_ev_cs_subevent_result_cont {
>         struct cs_step_data step_data[];
>  };
>
> +struct cs_subevent_result_data {
> +       uint16_t             start_acl_conn_evt_counter;
> +       uint16_t             freq_comp;
> +       int8_t               ref_pwr_lvl;
> +       uint8_t              num_ant_paths;
> +       uint8_t              subevent_abort_reason;
> +       uint64_t             timestamp_nanos;
> +       uint32_t             num_steps;
> +       struct cs_step_data *step_data;
> +};
> +
> +struct bcs_procedure_data {
> +       uint16_t procedure_counter;
> +       uint16_t procedure_sequence;
> +
> +       int8_t   initiator_selected_tx_power;
> +       int8_t   reflector_selected_tx_power;
> +
> +       struct cs_subevent_result_data *initiator_subevent_results;
> +       uint32_t initiator_subevent_count;
> +       uint8_t  initiator_procedure_abort_reason;
> +
> +       struct cs_subevent_result_data *reflector_subevent_results;
> +       uint32_t reflector_subevent_count;
> +       uint8_t  reflector_procedure_abort_reason;
> +
> +       struct rap_ev_cs_proc_enable_cmplt proc_enable_config;
> +       struct rap_ev_cs_config_cmplt      cs_config;
> +       uint8_t  t_sw_time_us_supported_by_local;
> +       uint8_t  t_sw_time_us_supported_by_remote;
> +       uint16_t ble_conn_interval;
> +};
>  typedef void (*bt_rap_debug_func_t)(const char *str, void *user_data);
>  typedef void (*bt_rap_ready_func_t)(struct bt_rap *rap, void *user_data);
>  typedef void (*bt_rap_destroy_func_t)(void *user_data);
>  typedef void (*bt_rap_func_t)(struct bt_rap *rap, void *user_data);
> +typedef void (*bt_rap_procedure_data_func_t)(struct bt_rap *rap,
> +                                       struct bcs_procedure_data *data,
> +                                       void *user_data);
>
>  struct bt_rap *bt_rap_ref(struct bt_rap *rap);
>  void bt_rap_unref(struct bt_rap *rap);
> @@ -177,6 +212,10 @@ bool bt_rap_set_user_data(struct bt_rap *rap, void *user_data);
>  bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
>                         void *user_data, bt_rap_destroy_func_t destroy);
>
> +bool bt_rap_set_procedure_data_cb(struct bt_rap *rap,
> +                               bt_rap_procedure_data_func_t cb,
> +                               void *user_data,
> +                               bt_rap_destroy_func_t destroy);
>  /* session related functions */
>  unsigned int bt_rap_register(bt_rap_func_t attached, bt_rap_func_t detached,
>                                         void *user_data);
> @@ -211,6 +250,10 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
>                         int8_t max_tx_power);
>  void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
>
> +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);
>  /* Connection handle mapping functions */
>  bool bt_rap_set_conn_hndl(void *hci_sm,
>                         struct bt_rap *rap,
> @@ -220,3 +263,7 @@ bool bt_rap_set_conn_hndl(void *hci_sm,
>                         bool is_central);
>
>  void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
> +/* CS capability sw_time and connection interval setters */
> +void bt_rap_set_local_sw_time(struct bt_rap *rap, uint8_t local_sw_time);
> +void bt_rap_set_remote_sw_time(struct bt_rap *rap, uint8_t remote_sw_time);
> +void bt_rap_set_conn_interval(struct bt_rap *rap, uint16_t conn_interval);
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* RE: arm64: dts: monaco-arduino-monza: Add support for LGA WiFi/BT module
From: bluez.test.bot @ 2026-07-16 16:55 UTC (permalink / raw)
  To: linux-bluetooth, loic.poulain
In-Reply-To: <20260716-monza-wireless-v4-1-9b02e6f549d7@oss.qualcomm.com>

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

This is an automated email and please do not reply to this email.

Dear Submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

----- Output -----

error: patch failed: drivers/power/sequencing/core.c:991
error: drivers/power/sequencing/core.c: patch does not apply
error: patch failed: include/linux/pwrseq/consumer.h:25
error: include/linux/pwrseq/consumer.h: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch

Please resolve the issue and submit the patches again.


---
Regards,
Linux Bluetooth


^ permalink raw reply

* Re: [PATCH BlueZ v1 0/2] Channel Sounding reflector GATT registration and
From: patchwork-bot+bluetooth @ 2026-07-16 16:40 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, luiz.dentz, quic_mohamull, quic_hbandi,
	quic_anubhavg
In-Reply-To: <20260710070820.3793624-1-naga.akella@oss.qualcomm.com>

Hello:

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

On Fri, 10 Jul 2026 12:38:18 +0530 you wrote:
> This series adds the initial plumbing required for Channel Sounding
> reflector support and RAP-side management of CS configuration parameters.
> 
> The first patch registers Channel Sounding GATT profiles on reflector
> devices before GATT client discovery checks, ensuring
> org.bluez.ChannelSounding1 interface is available
> when discovery is skipped.
> 
> [...]

Here is the summary with links:
  - [BlueZ,v1,1/2] rap: Add Channel Sounding parameter types and APIs
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=befde3661e03
  - [BlueZ,v1,2/2] src: Register GATT profiles for Channel Sounding reflector
    (no matching commit)

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



^ permalink raw reply

* Re: [PATCH BlueZ v1] client: Add scripts to start cs Initiator/reflector
From: patchwork-bot+bluetooth @ 2026-07-16 16:40 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, luiz.dentz, quic_mohamull, quic_hbandi,
	quic_anubhavg
In-Reply-To: <20260716064842.4016066-1-naga.akella@oss.qualcomm.com>

Hello:

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

On Thu, 16 Jul 2026 12:18:42 +0530 you wrote:
> This adds sample bluetoothctl scripts for cs initiator/
> reflector.
> 
> cs-initiator.bt sets up and runs a CS measurement as an Initiator
> cs-reflector.bt sets up the device to respond as a Reflector
> ---
>  client/scripts/cs-initiator.bt | 23 +++++++++++++++++++++++
>  client/scripts/cs-reflector.bt | 22 ++++++++++++++++++++++
>  2 files changed, 45 insertions(+)
>  create mode 100644 client/scripts/cs-initiator.bt
>  create mode 100644 client/scripts/cs-reflector.bt
> 
> [...]

Here is the summary with links:
  - [BlueZ,v1] client: Add scripts to start cs Initiator/reflector
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=efd216fe280b

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



^ permalink raw reply

* Re: [PATCH BlueZ v3 0/4] Add Channel Sounding Dbus support and
From: patchwork-bot+bluetooth @ 2026-07-16 16:40 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, luiz.dentz, quic_mohamull, quic_hbandi,
	quic_anubhavg
In-Reply-To: <20260712121743.2842547-1-naga.akella@oss.qualcomm.com>

Hello:

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

On Sun, 12 Jul 2026 17:47:39 +0530 you wrote:
> This series adds user-facing control and configuration support for
> Bluetooth LE Channel Sounding (CS) through a new DBus interface and
> bluetoothctl integration.
> 
> The RAP profile is extended to expose an
> org.bluez.ChannelSounding1 interface, allowing external applications
> to configure default Channel Sounding settings and start or stop
> measurement procedures. CS configuration, frequency, and default
> settings are moved from hardcoded values into shared parameter
> structures and accessor APIs, enabling runtime configuration from the
> DBus layer.
> 
> [...]

Here is the summary with links:
  - [BlueZ,v3,1/4] rap: Add Channel Sounding parameter types and APIs
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=befde3661e03
  - [BlueZ,v3,2/4] src: Register GATT profiles for Channel Sounding reflector
    (no matching commit)
  - [BlueZ,v3,3/4] profiles: Add D-Bus Channel Sounding control APIs
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=66d986081360
  - [BlueZ,v3,4/4] client: Add Channel Sounding shell submenu
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=79d1a58208e8

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



^ permalink raw reply

* [bluez/bluez] befde3: rap: Add Channel Sounding parameter types and APIs
From: Bhavani @ 2026-07-16 16:38 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/master
  Home:   https://github.com/bluez/bluez
  Commit: befde3661e03049f2c84288a658433ba1559b357
      https://github.com/bluez/bluez/commit/befde3661e03049f2c84288a658433ba1559b357
  Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
  Date:   2026-07-16 (Thu, 16 Jul 2026)

  Changed paths:
    M Makefile.am
    A src/shared/cs-types.h
    M src/shared/rap.h

  Log Message:
  -----------
  rap: Add Channel Sounding parameter types and APIs

Add shared Channel Sounding parameter structures
(bt_rap_le_cs_config, bt_rap_le_cs_frequency and
bt_rap_le_cs_default_settings) for exchanging
CS configuration, frequency, and default settings
between the RAP DBus layer and HCI state machine.

Add getter/setter APIs for CS config, frequency
and default settings, add measurement.


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

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

  Log Message:
  -----------
  profiles: Add D-Bus Channel Sounding control APIs

Expose org.bluez.ChannelSounding1 on the device path,
adding SetDefaultSettings, StartMeasurement, and StopMeasurement methods
and an Active property to let external clients configure and
control CS ranging sessions.

Replace hardcoded CS configuration values with configurable HCI accessors
(bt_rap_{get,set}_cs_config_params, bt_rap_{get,set}_cs_freq_params,
bt_rap_{get,set}_default_settings_params).
Add measurement control APIs (bt_rap_start_measurement,
bt_rap_stop_measurement) with optional timeout-based auto-stop
and callbacks for timeout and procedure-state changes,
allowing DBus Active state to reflect actual controller procedure status.


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

  Changed paths:
    M Makefile.tools
    A client/cs.c
    A client/cs.h
    M client/main.c

  Log Message:
  -----------
  client: Add Channel Sounding shell submenu

Add a cs bt_shell submenu for controlling
org.bluez.ChannelSounding1, supporting measurement start/stop,
default-setting configuration, and session status display.
Maintain reusable validated CS parameters and
track active sessions via D-Bus Active state,
replies, and disconnects.
Integrate with client/main.c to manage per-adapter
ChannelSounding proxies, device resolution,
property updates, and session cleanup.


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

  Changed paths:
    A client/scripts/cs-initiator.bt
    A client/scripts/cs-reflector.bt

  Log Message:
  -----------
  client: Add scripts to start cs Initiator/reflector

This adds sample bluetoothctl scripts for cs initiator/
reflector.

cs-initiator.bt sets up and runs a CS measurement as an Initiator
cs-reflector.bt sets up the device to respond as a Reflector


Compare: https://github.com/bluez/bluez/compare/ae5d5a1e3032...efd216fe280b

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