Linux bluetooth development
 help / color / mirror / Atom feed
* About Kernel limitation HCI_MAX_FRAME_SIZE (1024 + 4)
From: yjg0107 @ 2026-04-27  6:40 UTC (permalink / raw)
  To: luiz.dentz; +Cc: linux-bluetooth


[-- Attachment #1.1: Type: text/plain, Size: 662 bytes --]

Hi Luiz,

Question is same as https://github.com/bluez/bluez/issues/2073.
About Kernel limitation HCI_MAX_FRAME_SIZE (1024 + 4), you said that this will be fixed to enable user space changed, from #201 (comment).
The patch commit is torvalds/linux@09572fc, right?

However, why HCI_CHANNEL_USER type can not change MTU value? Google bluetooth aidl uses HCI_CHANNEL_USER type from https://cs.android.com/android/platform/superproject/+/android-latest-release:hardware/interfaces/bluetooth/aidl/default/net_bluetooth_mgmt.cpp line 282.

Could kernel remove the limitation that HCI_CHANNEL_USER can not change MTU?

Thanks.
B.R.
Jigong Yin



发自vivo电子邮件

[-- Attachment #1.2: Type: text/html, Size: 1643 bytes --]

^ permalink raw reply

* [PATCH BlueZ v3 0/3] Add RAS Packet format and Notification support
From: Prathibha Madugonde @ 2026-04-27 11:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

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

Changes in v3:
 Fix for cs_mode_one_data struct members rearrage as per spec
 Reading of cs_mode_one_data in rap_hci.c
 Address cs_mode data struct padding in src/shared/rap.c 

Changes in v2:
Fixed missing declaration in src/shared/rap.c

Patch overview:
  1/3 src/shared: Add RAS packet format and sending notifications to client
  2/3 uint/test-rap : Add PTS tests for CS reflector
  3/3 profiles/ranging: Read cs_mode_one_data members as per spec

 profiles/ranging/rap_hci.c |   25 +-
 src/shared/rap.c           | 1376 +++++++++++++++++++++++++++++++++++-
 src/shared/rap.h           |    4 +-
 unit/test-rap.c            |  264 ++++++-
 4 files changed, 1640 insertions(+), 29 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH BlueZ v3 1/3] Add RAS packet formatting and notification support for CS reflector
From: Prathibha Madugonde @ 2026-04-27 11:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427113544.1063560-1-prathm@qti.qualcomm.com>

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

Implement complete RAS data pipeline:
 - Handle HCI CS subevent result/continuation events
 - Serialize ranging/subevent headers per spec
 - GATT notifications for real-time ranging data

---
 src/shared/rap.c | 1376 +++++++++++++++++++++++++++++++++++++++++++++-
 src/shared/rap.h |    4 +-
 2 files changed, 1366 insertions(+), 14 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index ac6de04e0..5a08e023f 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -33,6 +33,170 @@
 /* Total number of attribute handles reserved for the RAS service */
 #define RAS_TOTAL_NUM_HANDLES		18
 
+/* 2(rc+cfg) + 1(tx_pwr) + 1(4 bits antenna_mask, 2 bits reserved,
+ * 2 bits pct_format)
+ */
+#define RAS_RANGING_HEADER_SIZE 4
+#define TOTAL_RAS_RANGING_HEADER_SIZE 5
+#define ATT_OVERHEAD 3 /* 1(opcode) + 2(char handle) */
+#define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
+#define RAS_SUBEVENT_HEADER_SIZE 8
+
+enum pct_format {
+	IQ = 0,
+	PHASE = 1,
+};
+
+enum ranging_done_status {
+	RANGING_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	RANGING_DONE_PARTIAL_RESULTS = 0x1,
+	RANGING_DONE_ABORTED = 0xF,
+};
+
+enum subevent_done_status {
+	SUBEVENT_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	SUBEVENT_DONE_PARTIAL_RESULTS = 0x1,
+	SUBEVENT_DONE_ABORTED = 0xF,
+};
+
+enum ranging_abort_reason {
+	RANGING_ABORT_NO_ABORT = 0x0,
+	RANGING_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	RANGING_ABORT_INSUFFICIENT_FILTERED_CHANNELS = 0x2,
+	RANGING_ABORT_INSTANT_HAS_PASSED = 0x3,
+	RANGING_ABORT_UNSPECIFIED = 0xF,
+};
+
+enum subevent_abort_reason {
+	SUBEVENT_ABORT_NO_ABORT = 0x0,
+	SUBEVENT_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	SUBEVENT_ABORT_NO_CS_SYNC_RECEIVED = 0x2,
+	SUBEVENT_ABORT_SCHEDULING_CONFLICTS_OR_LIMITED_RESOURCES = 0x3,
+	SUBEVENT_ABORT_UNSPECIFIED = 0xF,
+};
+
+/* Segmentation header: 1 byte
+ * bit 0: first_segment
+ * bit 1: last_segment
+ * bits 2-7: rolling_segment_counter (6 bits)
+ */
+struct segmentation_header {
+	uint8_t first_segment;
+	uint8_t last_segment;
+	uint8_t rolling_segment_counter;
+};
+
+/* Macros to pack/unpack segmentation header */
+#define SEG_HDR_PACK(first, last, counter) \
+	((uint8_t)(((first) ? 0x01 : 0x00) | \
+		((last) ? 0x02 : 0x00) | \
+		(((counter) & 0x3F) << 2)))
+
+#define SEG_HDR_UNPACK_FIRST(byte)    ((byte) & 0x01)
+#define SEG_HDR_UNPACK_LAST(byte)     (((byte) >> 1) & 0x01)
+#define SEG_HDR_UNPACK_COUNTER(byte)  (((byte) >> 2) & 0x3F)
+
+/* Ranging header: 4 bytes
+ * 0-1: ranging_counter (12 bits) | configuration_id (4 bits)
+ *      [little-endian]
+ * byte 2: selected_tx_power (signed)
+ * byte 3: antenna_paths_mask (4 bits) | reserved (2 bits) |
+ *         pct_format (2 bits)
+ */
+struct ranging_header {
+	uint16_t ranging_counter;
+	uint8_t configuration_id;
+	int8_t selected_tx_power;
+	uint8_t antenna_paths_mask;
+	uint8_t pct_format;
+} __packed;
+
+/* Macros to pack/unpack ranging header */
+#define RANGING_HDR_PACK_BYTE0_1(rc, cfg) \
+	((uint16_t)(((rc) & 0x0FFF) | (((cfg) & 0x0F) << 12)))
+
+#define RANGING_HDR_PACK_BYTE3(ant_mask, pct_fmt) \
+	((uint8_t)(((ant_mask) & 0x0F) | (((pct_fmt) & 0x03) << 6)))
+
+#define RANGING_HDR_UNPACK_RC(byte0_1) \
+	((uint16_t)((byte0_1) & 0x0FFF))
+
+#define RANGING_HDR_UNPACK_CFG(byte0_1) \
+	((uint8_t)(((byte0_1) >> 12) & 0x0F))
+
+#define RANGING_HDR_UNPACK_ANT_MASK(byte3) \
+	((uint8_t)((byte3) & 0x0F))
+
+#define RANGING_HDR_UNPACK_PCT_FMT(byte3) \
+	((uint8_t)(((byte3) >> 6) & 0x03))
+
+struct ras_subevent_header {
+	uint16_t start_acl_conn_event;
+	uint16_t frequency_compensation;
+	uint8_t ranging_done_status;
+	uint8_t subevent_done_status;
+	uint8_t ranging_abort_reason;
+	uint8_t subevent_abort_reason;
+	int8_t reference_power_level;
+	uint8_t num_steps_reported;
+};
+
+struct ras_subevent {
+	struct ras_subevent_header subevent_header;
+	uint8_t subevent_data[];
+};
+
+/* Role maps to Core CS roles (initiator/reflector) */
+enum cs_role {
+	CS_ROLE_INITIATOR = 0x00,
+	CS_ROLE_REFLECTOR = 0x01,
+};
+
+#define CS_INVALID_CONFIG_ID   0xFF
+/* 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
+};
+
+/* Main cs_procedure_data  */
+struct cs_procedure_data {
+	/* Identity and counters */
+	uint16_t counter;
+	uint8_t  num_antenna_paths;
+	/* Flags and status */
+	enum cs_procedure_done_status local_status;
+	enum cs_procedure_done_status remote_status;
+	bool contains_complete_subevent_;
+	/* RAS aggregation */
+	struct segmentation_header segmentation_header_;
+	struct ranging_header      ranging_header_;
+	struct iovec       ras_raw_data_;        /* raw concatenated */
+	uint16_t           ras_raw_data_index_;
+	struct ras_subevent_header  ras_subevent_header_;
+	struct iovec       ras_subevent_data_;   /* buffer per subevent */
+	uint8_t            ras_subevent_counter_;
+	/* Reference power levels */
+	int8_t initiator_reference_power_level;
+	int8_t reflector_reference_power_level;
+	bool ranging_header_prepended_;
+	bool ras_subevent_header_emitted;
+};
+
+struct cstracker {
+	enum cs_role        role;                 /* INITIATOR/REFLECTOR */
+	uint8_t             config_id;            /* CS_INVALID_CONFIG_ID */
+	uint8_t             selected_tx_power;    /* PROC_ENABLE_COMPLETE */
+	uint8_t             rtt_type;             /* RTT type */
+	struct cs_procedure_data *current_proc;
+	/* Cached header values for CONT events (per-connection state) */
+	uint16_t last_proc_counter;
+	uint16_t last_start_acl_conn_evt_counter;
+	uint16_t last_freq_comp;
+	int8_t last_ref_pwr_lvl;
+};
+
 /* Ranging Service context */
 struct ras {
 	struct bt_rap_db *rapdb;
@@ -43,9 +207,17 @@ struct ras {
 	struct gatt_db_attribute *realtime_chrc;
 	struct gatt_db_attribute *realtime_chrc_ccc;
 	struct gatt_db_attribute *ondemand_chrc;
+	struct gatt_db_attribute *ondemand_ccc;
 	struct gatt_db_attribute *cp_chrc;
+	struct gatt_db_attribute *cp_ccc;
 	struct gatt_db_attribute *ready_chrc;
+	struct gatt_db_attribute *ready_ccc;
 	struct gatt_db_attribute *overwritten_chrc;
+	struct gatt_db_attribute *overwritten_ccc;
+
+	/* CCC state tracking for mutual exclusivity */
+	uint16_t realtime_ccc_value;
+	uint16_t ondemand_ccc_value;
 };
 
 struct bt_rap_db {
@@ -70,6 +242,7 @@ struct bt_rap {
 	bt_rap_destroy_func_t debug_destroy;
 	void *debug_data;
 	void *user_data;
+	struct cstracker *resptracker;
 };
 
 static struct queue *rap_db;
@@ -90,6 +263,209 @@ struct bt_rap_ready {
 	void *data;
 };
 
+uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
+uint8_t ras_segment_header_size = 1;
+
+static struct cs_procedure_data *cs_procedure_data_create(
+					uint16_t procedure_counter,
+					uint8_t num_antenna_paths,
+					uint8_t configuration_id,
+					uint8_t selected_tx_power)
+{
+	struct cs_procedure_data *d;
+	uint8_t i;
+
+	d = calloc(1, sizeof(struct cs_procedure_data));
+
+	if (!d)
+		return NULL;
+
+	d->counter = procedure_counter;
+	d->num_antenna_paths = num_antenna_paths;
+	d->local_status = CS_PROC_PARTIAL_RESULTS;
+	d->remote_status = CS_PROC_PARTIAL_RESULTS;
+	d->contains_complete_subevent_ = false;
+	d->segmentation_header_.first_segment = 1;
+	d->segmentation_header_.last_segment = 0;
+	d->segmentation_header_.rolling_segment_counter = 0;
+	d->ranging_header_.ranging_counter = procedure_counter;
+	d->ranging_header_.configuration_id = configuration_id;
+	d->ranging_header_.selected_tx_power = selected_tx_power;
+	d->ranging_header_.antenna_paths_mask = 0;
+
+	for (i = 0; i < num_antenna_paths; i++)
+		d->ranging_header_.antenna_paths_mask |= (1u << i);
+
+	d->ranging_header_.pct_format = IQ;
+	memset(&d->ras_raw_data_, 0, sizeof(d->ras_raw_data_));
+	d->ras_raw_data_index_ = 0;
+	memset(&d->ras_subevent_data_, 0, sizeof(d->ras_subevent_data_));
+	d->ras_subevent_counter_ = 0;
+	d->initiator_reference_power_level = 0;
+	d->reflector_reference_power_level = 0;
+	d->ranging_header_prepended_ = false;
+	d->ras_subevent_header_emitted = false;
+
+	return d;
+}
+
+static void cs_procedure_data_destroy(struct cs_procedure_data *d)
+{
+	if (!d)
+		return;
+
+	free(d->ras_raw_data_.iov_base);
+	free(d->ras_subevent_data_.iov_base);
+	free(d);
+}
+
+static void cs_pd_set_local_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->local_status = s;
+}
+
+static void cs_pd_set_remote_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->remote_status = s;
+}
+
+static void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
+					int8_t init_lvl, int8_t ref_lvl)
+{
+	if (!d)
+		return;
+
+	d->initiator_reference_power_level = init_lvl;
+	d->reflector_reference_power_level = ref_lvl;
+}
+
+static void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
+				uint16_t start_acl_conn_event,
+				uint16_t frequency_compensation,
+				int8_t reference_power_level)
+{
+	if (!d)
+		return;
+
+	d->ras_subevent_counter_++;
+	d->ras_subevent_header_.start_acl_conn_event = start_acl_conn_event;
+	d->ras_subevent_header_.frequency_compensation =
+		frequency_compensation;
+	d->ras_subevent_header_.reference_power_level = reference_power_level;
+	d->ras_subevent_header_.num_steps_reported = 0;
+	d->ras_subevent_header_emitted = false;
+	d->ras_subevent_data_.iov_len = 0;
+}
+
+static bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
+					const uint8_t *bytes, size_t len)
+{
+	if (!d || !bytes || len == 0)
+		return false;
+
+	return util_iov_append(&d->ras_subevent_data_, bytes, len) != NULL;
+}
+
+static inline size_t serialize_ras_subevent_header(
+				const struct ras_subevent_header *h,
+				uint8_t *out, size_t out_len)
+{
+	uint16_t start_le;
+	uint16_t freq_le;
+
+	if (!h || !out || out_len < RAS_SUBEVENT_HEADER_SIZE)
+		return 0;
+
+	start_le = (uint16_t)h->start_acl_conn_event;
+	out[0] = (uint8_t)(start_le & 0xFF);
+	out[1] = (uint8_t)((start_le >> 8) & 0xFF);
+
+	freq_le = (uint16_t)h->frequency_compensation;
+	out[2] = (uint8_t)(freq_le & 0xFF);
+	out[3] = (uint8_t)((freq_le >> 8) & 0xFF);
+
+	out[4] = (uint8_t)((h->ranging_done_status & 0x0F) |
+			((h->subevent_done_status & 0x0F) << 4));
+
+	out[5] = (uint8_t)((h->ranging_abort_reason & 0x0F) |
+			((h->subevent_abort_reason & 0x0F) << 4));
+
+	out[6] = (uint8_t)h->reference_power_level;
+	out[7] = (uint8_t)h->num_steps_reported;
+
+	return RAS_SUBEVENT_HEADER_SIZE;
+}
+
+static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
+				uint8_t num_steps_reported,
+				uint8_t ranging_done_status,
+				uint8_t subevent_done_status,
+				uint8_t ranging_abort_reason,
+				uint8_t subevent_abort_reason)
+{
+	size_t hdr_sz;
+	size_t payload_sz;
+	size_t total;
+	uint8_t *buf;
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	d->ras_subevent_header_.num_steps_reported =
+		(uint8_t)(d->ras_subevent_header_.num_steps_reported +
+			num_steps_reported);
+	d->ras_subevent_header_.ranging_done_status = ranging_done_status;
+	d->ras_subevent_header_.subevent_done_status = subevent_done_status;
+	d->ras_subevent_header_.ranging_abort_reason = ranging_abort_reason;
+	d->ras_subevent_header_.subevent_abort_reason = subevent_abort_reason;
+
+	if (subevent_done_status == SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
+		d->contains_complete_subevent_ = true;
+
+	if (subevent_done_status == SUBEVENT_DONE_PARTIAL_RESULTS)
+		return true;
+
+	if (!d->ras_subevent_header_emitted) {
+		hdr_sz = RAS_SUBEVENT_HEADER_SIZE;
+		payload_sz = d->ras_subevent_data_.iov_len;
+		total = hdr_sz + payload_sz;
+		buf = (uint8_t *)malloc(total);
+
+		if (!buf)
+			return false;
+
+		w = serialize_ras_subevent_header(&d->ras_subevent_header_,
+						buf, total);
+
+		if (w != hdr_sz) {
+			free(buf);
+			return false;
+		}
+
+		if (payload_sz > 0)
+			memcpy(buf + hdr_sz,
+				(const uint8_t *)d->ras_subevent_data_.iov_base,
+				payload_sz);
+
+		ok = util_iov_append(&d->ras_raw_data_, buf, total) != NULL;
+		free(buf);
+
+		if (!ok)
+			return false;
+
+		d->ras_subevent_data_.iov_len = 0;
+		d->ras_subevent_header_emitted = true;
+	}
+
+	return true;
+}
+
 static struct ras *rap_get_ras(struct bt_rap *rap)
 {
 	if (!rap)
@@ -155,6 +531,11 @@ static void rap_free(void *data)
 
 	rap_db_free(rap->rrapdb);
 
+	if (rap->resptracker) {
+		free(rap->resptracker);
+		rap->resptracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -240,6 +621,22 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 	return true;
 }
 
+static void cs_tracker_init(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	memset(t, 0, sizeof(*t));
+	t->role = CS_ROLE_REFLECTOR;
+	t->config_id = CS_INVALID_CONFIG_ID;
+	t->rtt_type = 0;
+	t->selected_tx_power = 0;
+	t->last_proc_counter = 0;
+	t->last_start_acl_conn_evt_counter = 0;
+	t->last_freq_comp = 0;
+	t->last_ref_pwr_lvl = 0;
+}
+
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
 				 unsigned int id, uint16_t offset,
 				 uint8_t opcode, struct bt_att *att,
@@ -304,6 +701,225 @@ static void ras_data_overwritten_read_cb(struct gatt_db_attribute *attrib,
 	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
 }
 
+/* CCC write callbacks for custom handling */
+static void ras_realtime_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable realtime
+	 * while ondemand is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->ondemand_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->realtime_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ondemand_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+				BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 && ccc_value != 0x0002 &&
+	    ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable ondemand
+	 * while realtime is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->realtime_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->ondemand_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_cp_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ready_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_overwritten_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
 /* Service registration – store attribute pointers */
 static struct ras *register_ras_service(struct gatt_db *db)
 {
@@ -349,9 +965,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  NULL, NULL, ras);
 
 	ras->realtime_chrc_ccc =
-		gatt_db_service_add_ccc(ras->svc,
-					BT_ATT_PERM_READ |
-					BT_ATT_PERM_WRITE);
+		gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_realtime_ccc_write_cb, ras);
 
 	/* On-demand Ranging Data */
 	bt_uuid16_create(&uuid, RAS_ONDEMAND_DATA_UUID);
@@ -364,8 +980,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_ondemand_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ondemand_ccc_write_cb, ras);
 
 	/* RAS Control Point */
 	bt_uuid16_create(&uuid, RAS_CONTROL_POINT_UUID);
@@ -379,8 +996,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_control_point_write_cb,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_cp_ccc_write_cb, ras);
 
 	/* RAS Data Ready */
 	bt_uuid16_create(&uuid, RAS_DATA_READY_UUID);
@@ -394,8 +1012,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_ready_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ready_ccc_write_cb, ras);
 
 	/* RAS Data Overwritten */
 	bt_uuid16_create(&uuid, RAS_DATA_OVERWRITTEN_UUID);
@@ -409,8 +1028,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_overwritten_read_cb,
 						  NULL, ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_overwritten_ccc_write_cb, ras);
 
 	/* Activate the service */
 	gatt_db_service_set_active(ras->svc, true);
@@ -503,32 +1123,746 @@ bool bt_rap_unregister(unsigned int id)
 	return true;
 }
 
+static inline size_t serialize_segmentation_header(
+				const struct segmentation_header *s,
+				uint8_t *out, size_t out_len)
+{
+	if (!s || !out || out_len < 1)
+		return 0;
+
+	/* [0] bit0: first, bit1: last, bits2..7: rolling counter */
+	out[0] = (s->first_segment ? 0x01 : 0x00) |
+		(s->last_segment ? 0x02 : 0x00) |
+		((s->rolling_segment_counter & 0x3F) << 2);
+
+	return 1;
+}
+
+static inline size_t serialize_ranging_header(const struct ranging_header *r,
+						uint8_t *out, size_t out_len)
+{
+	uint16_t rcid;
+
+	if (!r || !out || out_len < RAS_RANGING_HEADER_SIZE)
+		return 0;
+
+	/* Little-endian pack: [rc (12 bits) | cfg_id (4 bits)] */
+	rcid = RANGING_HDR_PACK_BYTE0_1(r->ranging_counter,
+					r->configuration_id);
+
+	put_le16(rcid, out + 0);
+	out[2] = (uint8_t)r->selected_tx_power;
+	/* Byte 3: antenna_paths_mask | reserved | pct_format */
+	out[3] = RANGING_HDR_PACK_BYTE3(r->antenna_paths_mask,
+					r->pct_format);
+
+	return RAS_RANGING_HEADER_SIZE;
+}
+
+static inline uint16_t ras_att_value_payload_max(struct bt_rap *rap)
+{
+	struct bt_att *att = bt_rap_get_att(rap);
+	uint16_t mtu = att ? bt_att_get_mtu(att) : default_ras_mtu;
+
+	return (uint16_t)(mtu > ATT_OVERHEAD ?
+		(mtu - ATT_OVERHEAD - TOTAL_RAS_RANGING_HEADER_SIZE -
+		ras_segment_header_size) : 0);
+}
+
+/* Prepend data to an iovec */
+static bool iov_prepend_bytes(struct iovec *iov, const uint8_t *bytes,
+				size_t len)
+{
+	size_t new_len;
+	void *new_base;
+
+	if (!iov || !bytes || len == 0)
+		return false;
+
+	new_len = iov->iov_len + len;
+	new_base = malloc(new_len);
+
+	if (!new_base)
+		return false;
+
+	memcpy(new_base, bytes, len);
+
+	if (iov->iov_len > 0)
+		memcpy((uint8_t *)new_base + len, iov->iov_base,
+			iov->iov_len);
+
+	free(iov->iov_base);
+	iov->iov_base = new_base;
+	iov->iov_len = new_len;
+
+	return true;
+}
+
+/* Append the 4-byte RangingHeader to ras_raw_data_ on first segment */
+static bool ras_maybe_prepend_ranging_header(struct cs_procedure_data *d)
+{
+	uint8_t hdr[RAS_RANGING_HEADER_SIZE];
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	if (d->ranging_header_prepended_)
+		return false;
+
+	if (!d->segmentation_header_.first_segment)
+		return false;
+
+	if (d->ras_raw_data_index_ != 0)
+		return false;
+
+	w = serialize_ranging_header(&d->ranging_header_, hdr, sizeof(hdr));
+
+	if (w != RAS_RANGING_HEADER_SIZE)
+		return false;
+
+	ok = iov_prepend_bytes(&d->ras_raw_data_, hdr, w);
+
+	if (ok)
+		d->ranging_header_prepended_ = true;
+
+	return ok;
+}
+
+static void send_ras_segment_data(struct bt_rap *rap,
+				struct cs_procedure_data *proc)
+{
+	struct ras *ras;
+	uint16_t value_max;
+	const uint16_t header_len = ras_segment_header_size;
+	uint16_t raw_payload_size;
+	bool ok;
+
+	if (!rap || !proc)
+		return;
+
+	if (!rap->lrapdb || !rap->lrapdb->ras)
+		return;
+	ras = rap->lrapdb->ras;
+	value_max = ras_att_value_payload_max(rap);
+
+	if (value_max == 0) {
+		DBG(rap, "value_max=0 (MTU not available?)");
+		return;
+	}
+
+	if (value_max <= header_len) {
+		DBG(rap, "value_max(%u) too small for header", value_max);
+		return;
+	}
+
+	raw_payload_size = (uint16_t)(value_max - header_len);
+
+	/* Convert tail recursion to loop */
+	while (true) {
+		size_t total_len = proc->ras_raw_data_.iov_len;
+		size_t index = proc->ras_raw_data_index_;
+		size_t unsent_data_size;
+		uint16_t copy_size;
+		uint16_t seg_len;
+		uint8_t *seg;
+		uint16_t wr = 0;
+
+		if (index > total_len)
+			index = total_len;
+
+		unsent_data_size = total_len - index;
+
+		if (unsent_data_size == 0)
+			return;
+
+		/* Set last_segment if procedure complete or fits in segment */
+		if ((proc->local_status != CS_PROC_PARTIAL_RESULTS &&
+				unsent_data_size <= raw_payload_size) ||
+			(proc->contains_complete_subevent_ &&
+				unsent_data_size <= raw_payload_size)) {
+			proc->segmentation_header_.last_segment = 1;
+		} else {
+			proc->segmentation_header_.last_segment = 0;
+		}
+
+		/* Wait for more data if needed and not last segment */
+		if (unsent_data_size < raw_payload_size &&
+				proc->segmentation_header_.last_segment == 0) {
+			DBG(rap, "waiting for more data (unsent=%zu < "
+				"payload=%u)", unsent_data_size,
+				raw_payload_size);
+			return;
+		}
+
+		copy_size = (uint16_t)((unsent_data_size < raw_payload_size) ?
+				unsent_data_size : raw_payload_size);
+		seg_len = (uint16_t)(header_len + copy_size);
+		seg = (uint8_t *)malloc(seg_len);
+
+		if (!seg) {
+			DBG(rap, "OOM (%u)", seg_len);
+			return;
+		}
+
+		wr += (uint16_t)serialize_segmentation_header(
+				&proc->segmentation_header_, seg + wr,
+				seg_len - wr);
+		memcpy(seg + wr,
+			(const uint8_t *)proc->ras_raw_data_.iov_base + index,
+			copy_size);
+		wr += copy_size;
+
+		/* Try sending to real-time characteristic */
+		if (ras->realtime_chrc)
+			ok = gatt_db_attribute_notify(ras->realtime_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		/* Try sending to on-demand characteristic */
+		if (ras->ondemand_chrc)
+			ok = gatt_db_attribute_notify(ras->ondemand_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		free(seg);
+
+		if (!ok) {
+			DBG(rap, "Failed to send RAS notification");
+			return;
+		}
+
+		/* Advance read cursor and update segmentation state */
+		proc->ras_raw_data_index_ += copy_size;
+		proc->segmentation_header_.first_segment = 0;
+		proc->segmentation_header_.rolling_segment_counter =
+			(uint8_t)((proc->segmentation_header_
+				.rolling_segment_counter + 1) & 0x3F);
+
+		if (proc->segmentation_header_.last_segment ||
+				proc->ras_raw_data_index_ >=
+				proc->ras_raw_data_.iov_len) {
+			DBG(rap, "RAS clear ras buffers");
+			proc->ras_raw_data_.iov_len = 0;
+			proc->ras_raw_data_index_ = 0;
+			proc->ranging_header_prepended_ = false;
+			return;
+		}
+	}
+}
+
+static inline void resptracker_reset_current_proc(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	if (t->current_proc) {
+		cs_procedure_data_destroy(t->current_proc);
+		t->current_proc = NULL;
+	}
+}
+/* Unified local subevent handler */
+static void handle_local_subevent_result(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 void *step_bytes)
+{
+	struct cstracker *resptracker;
+	struct cs_procedure_data *proc;
+	const struct cs_step_data *steps;
+	uint8_t idx;
+
+	if (!rap || !rap->resptracker || !step_bytes)
+		return;
+
+	resptracker = rap->resptracker;
+
+	if (resptracker->current_proc) {
+		struct cs_procedure_data *cur = resptracker->current_proc;
+
+		if (has_header_fields && cur->counter != proc_counter) {
+			/* Safety: a new procedure; destroy the previous one */
+			resptracker_reset_current_proc(resptracker);
+		}
+	}
+
+	proc = resptracker->current_proc;
+	/* Cache header info from a RESULT event for later CONT usage */
+	if (has_header_fields) {
+		resptracker->last_proc_counter = proc_counter;
+		resptracker->last_start_acl_conn_evt_counter =
+			start_acl_conn_evt_counter;
+		resptracker->last_freq_comp = freq_comp;
+		resptracker->last_ref_pwr_lvl = ref_pwr_lvl;
+	}
+
+	/* Create the procedure on first use */
+	if (!proc) {
+		uint16_t create_counter = has_header_fields ? proc_counter :
+					resptracker->last_proc_counter;
+
+		proc = cs_procedure_data_create(create_counter,
+						num_ant_paths,
+						config_id,
+						resptracker->selected_tx_power);
+		if (!proc)
+			return;
+
+		resptracker->current_proc = proc;
+
+		/* Reference power levels and status defaults */
+		cs_pd_set_reference_power_levels(proc,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl);
+		cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+		cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+	}
+
+	/* Begin a new RAS subevent only when we have header fields */
+	if (has_header_fields) {
+		cs_pd_ras_begin_subevent(proc,
+					start_acl_conn_evt_counter,
+					freq_comp,
+					ref_pwr_lvl);
+	}
+
+	/* step_bytes points to an array of struct cs_step_data */
+	steps = (const struct cs_step_data *)step_bytes;
+
+	/* Process each step */
+	for (idx = 0; idx < num_steps_reported; idx++) {
+		const struct cs_step_data *step = &steps[idx];
+		const uint8_t mode = step->step_mode;
+		const uint8_t channel = step->step_chnl;
+		const uint8_t payload_len = step->step_data_length;
+		uint8_t mode_byte;
+		uint8_t mode_type;
+		const uint8_t *payload;
+		uint8_t plen;
+		bool step_aborted;
+
+		/* Check if step is aborted: bit 7 of step_mode or
+		 * 0 payload len
+		 */
+		step_aborted = (mode & RAS_STEP_ABORTED_BIT) ||
+			(payload_len == 0);
+
+		DBG(rap, "step[%u]: mode=0x%02x channel=%u payload_len=%u "
+			"aborted=%s", idx, mode, channel, payload_len,
+			step_aborted ? "YES" : "NO");
+
+		/* Slim-step serialization for RAS:
+		 * - 1 byte: mode (bit7 set if aborted)
+		 * - payload: exactly step_data_length bytes (raw mode data)
+		 */
+		mode_byte = step->step_mode;
+		payload = (const uint8_t *)&step->step_mode_data;
+		plen = step->step_data_length;
+
+		if (step_aborted) {
+			/* Ensure abort bit is set */
+			mode_byte |= RAS_STEP_ABORTED_BIT;
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* No payload when aborted - per RAS spec Table 3.8 */
+			DBG(rap, "step[%u]: mode=0x%02x aborted, "
+				"no payload sent", idx, mode_byte);
+		} else {
+			mode_type = mode & 0x03;
+
+			/* Mode byte first (without abort bit) */
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+
+			switch (mode_type) {
+			case CS_MODE_ZERO: {
+				/* Mode 0: use raw structure bytes */
+				payload =
+					(const uint8_t *)&step->step_mode_data;
+				plen = step->step_data_length;
+				cs_pd_ras_append_subevent_bytes(proc,
+								payload, plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode0 "
+					"payload_len=%u sent",
+					idx, mode_byte, (unsigned int)plen);
+				break;
+			}
+			case CS_MODE_ONE: {
+				const struct cs_mode_one_data *m1 =
+					&step->step_mode_data.mode_one_data;
+				uint8_t buf[16];  /* Max Mode 1 size */
+				uint8_t *p = buf;
+				uint16_t time_val;
+				enum cs_role cs_role = resptracker->role;
+				uint8_t cs_rtt_type = resptracker->rtt_type;
+
+				*p++ = m1->packet_quality;
+				*p++ = m1->packet_nadm;
+				*p++ = m1->packet_rssi_dbm;
+
+				/* Time value (2 bytes LE) - use the
+				 * appropriate field based on role
+				 */
+				if (cs_role == CS_ROLE_REFLECTOR)
+					time_val = m1->tod_toa_refl;
+				else
+					time_val = m1->toa_tod_init;
+
+				*p++ = (uint8_t)(time_val & 0xFF);
+				*p++ = (uint8_t)((time_val >> 8) & 0xFF);
+
+				*p++ = m1->packet_ant;
+
+				/* PCT samples if RTT type contains
+				 * sounding sequence
+				 */
+				if (cs_rtt_type == 0x01 ||
+						cs_rtt_type == 0x02) {
+					uint32_t pct1 =
+						((uint32_t)(m1->packet_pct1
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct1
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct1 & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 16) & 0xFF);
+
+					/* PCT2 (3 bytes) */
+					uint32_t pct2 =
+						((uint32_t)(m1->packet_pct2
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct2
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct2 & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 16) & 0xFF);
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode1 "
+					"serialized payload_len=%u role=%s "
+					"rtt_type=0x%02x",
+					idx, mode_byte, (unsigned int)plen,
+					cs_role == CS_ROLE_INITIATOR ?
+						"INIT" : "REFL", cs_rtt_type);
+				break;
+			}
+			case CS_MODE_TWO: {
+				const struct cs_mode_two_data *m2 =
+					&step->step_mode_data.mode_two_data;
+				/* Max Mode 2 size: 1 + 5*(3+1) = 21 bytes */
+				uint8_t buf[64];
+				uint8_t *p = buf;
+				uint8_t k;
+				uint8_t num_paths = (num_ant_paths + 1) < 5 ?
+					(num_ant_paths + 1) : 5;
+
+				*p++ = m2->ant_perm_index;
+
+				/* Serialize each path: PCT (3 bytes) +
+				 * quality (1 byte)
+				 */
+				for (k = 0; k < num_paths; k++) {
+					/* Convert 4-byte structure PCT to
+					 * 3-byte wire format
+					 */
+					uint32_t pct =
+						((uint32_t)(m2->tone_pct[k]
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m2->tone_pct[k]
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct & 0xFF);
+					*p++ = (uint8_t)((pct >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct >> 16) & 0xFF);
+
+					*p++ = m2->tone_quality_indicator[k];
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode2 "
+					"serialized payload_len=%u paths=%u",
+					idx, mode_byte, (unsigned int)plen,
+					num_paths);
+				break;
+			}
+			case CS_MODE_THREE: {
+				const struct cs_mode_three_data *m3 =
+					&step->step_mode_data.mode_three_data;
+				const struct cs_mode_one_data *m1 =
+					&m3->mode_one_data;
+				const struct cs_mode_two_data *m2 =
+					&m3->mode_two_data;
+				uint8_t buf[80];  /* Max Mode 3 size */
+				uint8_t *p = buf;
+				uint8_t k;
+				uint8_t num_paths = (num_ant_paths + 1) < 5 ?
+					(num_ant_paths + 1) : 5;
+				uint8_t cs_rtt_type = resptracker->rtt_type;
+				enum cs_role cs_role = resptracker->role;
+				uint16_t time_val;
+				bool include_pct;
+
+				/* Determine if PCT samples should be
+				 * included
+				 */
+				include_pct = (cs_rtt_type == 0x01 ||
+						cs_rtt_type == 0x02) &&
+					(cs_role == CS_ROLE_INITIATOR ||
+						cs_role == CS_ROLE_REFLECTOR);
+
+				/* Mode 1 portion - basic fields */
+				*p++ = m1->packet_quality;
+				*p++ = m1->packet_nadm;
+				*p++ = m1->packet_rssi_dbm;
+
+				/* Time value based on role */
+				if (cs_role == CS_ROLE_REFLECTOR)
+					time_val = m1->tod_toa_refl;
+				else
+					time_val = m1->toa_tod_init;
+
+				*p++ = (uint8_t)(time_val & 0xFF);
+				*p++ = (uint8_t)((time_val >> 8) & 0xFF);
+
+				*p++ = m1->packet_ant;
+
+				/* PCT samples if conditions met */
+				if (include_pct) {
+					uint32_t pct1 =
+						((uint32_t)(m1->packet_pct1
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct1
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct1 & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 16) & 0xFF);
+
+					uint32_t pct2 =
+						((uint32_t)(m1->packet_pct2
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct2
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct2 & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 16) & 0xFF);
+				}
+
+				/* Mode 2 portion */
+				*p++ = m2->ant_perm_index;
+
+				for (k = 0; k < num_paths; k++) {
+					uint32_t pct =
+						((uint32_t)(m2->tone_pct[k]
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m2->tone_pct[k]
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct & 0xFF);
+					*p++ = (uint8_t)((pct >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct >> 16) & 0xFF);
+
+					*p++ = m2->tone_quality_indicator[k];
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode3 "
+					"serialized payload_len=%u paths=%u "
+					"role=%s rtt_type=0x%02x pct=%s",
+					idx, mode_byte, (unsigned int)plen,
+					num_paths,
+					cs_role == CS_ROLE_INITIATOR ?
+						"INIT" : "REFL",
+					cs_rtt_type,
+					include_pct ? "YES" : "NO");
+				break;
+			}
+			default:
+				/* Unknown mode: use raw structure bytes */
+				payload =
+					(const uint8_t *)&step->step_mode_data;
+				plen = step->step_data_length;
+				cs_pd_ras_append_subevent_bytes(proc, payload,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x unknown mode, "
+					"payload_len=%u sent",
+					idx, mode_byte, (unsigned int)plen);
+				break;
+			}
+		}
+	}
+
+	/* Update status for this chunk */
+	cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+	cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+
+	/* Commit subevent chunk (RESULT or CONT) */
+	cs_pd_ras_commit_subevent(proc,
+		num_steps_reported,
+		proc_done_status,
+		subevt_done_status,
+		abort_reason & 0x0F,
+		(abort_reason >> 4) & 0x0F);
+
+	/* Ensure first segment body starts with the 4-byte RangingHeader */
+	ras_maybe_prepend_ranging_header(proc);
+
+	if (subevt_done_status != SUBEVENT_DONE_PARTIAL_RESULTS)
+		/* Send RAS raw segment data */
+		send_ras_segment_data(rap, proc);
+
+	/* Procedure complete? Clean up */
+	if (proc_done_status == CS_PROC_ALL_RESULTS_COMPLETE) {
+		DBG(rap, "Destroying CsProcedureData counter=%u and "
+			"clearing current_proc", proc->counter);
+		resptracker_reset_current_proc(resptracker);
+		/* Reset cached header values for next procedure */
+		resptracker->last_proc_counter = 0;
+		resptracker->last_start_acl_conn_evt_counter = 0;
+		resptracker->last_freq_comp = 0;
+		resptracker->last_ref_pwr_lvl = 0;
+	}
+}
+
+static void form_ras_data_with_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->resptracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+	handle_local_subevent_result(rap,
+		true,			/* has header fields */
+		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);	/* start of steps */
+}
+static void form_ras_data_with_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 *resptracker;
+
+	if (!rap || !rap->resptracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+
+	resptracker = rap->resptracker;
+
+	/* Use cached header values captured from the last RESULT event */
+	handle_local_subevent_result(rap,
+		false,			/* CONT has no header fields */
+		cont->config_id,
+		cont->num_ant_paths,
+		resptracker->last_proc_counter,
+		resptracker->last_start_acl_conn_evt_counter,
+		resptracker->last_freq_comp,
+		resptracker->last_ref_pwr_lvl,
+		cont->proc_done_status,
+		cont->subevt_done_status,
+		cont->abort_reason,
+		cont->num_steps_reported,
+		cont->step_data);
+}
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_subevent_result_cont *cont = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
+
+	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_subevent_result *data = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent: len=%d", length);
+
+	/* Populate CsProcedureData and send RAS payload */
+	form_ras_data_with_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_proc_enable_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
 
 	DBG(rap, "Received CS procedure enable complete subevent: len=%d",
 	    length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Populate responder tracker */
+	resptracker->config_id = data->config_id;
+	resptracker->selected_tx_power = data->sel_tx_pwr;
 }
 
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
@@ -544,9 +1878,27 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
+
+	if (!rap)
+		return;
 
 	DBG(rap, "Received CS config complete subevent: len=%d", length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Basic fields */
+	resptracker->config_id = data->config_id;
+	resptracker->role = data->role;
+	resptracker->rtt_type = data->rtt_type;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -786,7 +2138,7 @@ bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client)
 
 	bt_uuid16_create(&uuid, RAS_UUID16);
 
-	gatt_db_foreach_service(rap->lrapdb->db, &uuid,
+	gatt_db_foreach_service(rap->rrapdb->db, &uuid,
 				foreach_rap_service, rap);
 
 	return true;
diff --git a/src/shared/rap.h b/src/shared/rap.h
index 15ddea295..c9431aecd 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -97,11 +97,11 @@ struct cs_mode_zero_data {
 
 struct cs_mode_one_data {
 	uint8_t packet_quality;
-	uint8_t packet_rssi_dbm;
-	uint8_t packet_ant;
 	uint8_t packet_nadm;
+	uint8_t packet_rssi_dbm;
 	int16_t toa_tod_init;
 	int16_t tod_toa_refl;
+	uint8_t packet_ant;
 	struct pct_iq_sample packet_pct1;
 	struct pct_iq_sample packet_pct2;
 };
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v3 2/3] unit/test-rap: Add PTS tests for CS reflector case
From: Prathibha Madugonde @ 2026-04-27 11:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427113544.1063560-1-prathm@qti.qualcomm.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 11091 bytes --]

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

Added below RAS - Real time Ranging PTS testcases
RAS/SR/RCO/BV-01-C [Characteristic Read – RAS Features]
RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
---
 unit/test-rap.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 260 insertions(+), 4 deletions(-)

diff --git a/unit/test-rap.c b/unit/test-rap.c
index 884cb1c96..6f65967a4 100644
--- a/unit/test-rap.c
+++ b/unit/test-rap.c
@@ -37,6 +37,7 @@ struct test_data_ras {
 	size_t iovcnt;
 	struct iovec *iov;
 	unsigned int ras_id;
+	struct bt_rap *rap;  /* Store rap instance for CS injection */
 };
 
 struct test_data_rap {
@@ -68,8 +69,8 @@ struct notify {
 	do {							\
 		const struct iovec iov[] = { args };		\
 		static struct test_data_ras data;			\
-		data.iovcnt = ARRAY_SIZE(iov_data(args));	\
-		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
+		data.iovcnt = ARRAY_SIZE(iov);	\
+		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov)); \
 		tester_add(name, &data, NULL, function,	\
 				test_teardown_ras);			\
 	} while (0)
@@ -155,6 +156,94 @@ static void gatt_notify_cb(struct gatt_db_attribute *attrib,
 		printf("%s: Failed to send notification\n", __func__);
 }
 
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct test_data_ras *data = user_data;
+	struct ccc_state *ccc;
+	uint16_t handle;
+	uint16_t ccc_value;
+	uint8_t ecode = 0;
+
+	handle = gatt_db_attribute_get_handle(attrib);
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	ccc_value = get_le16(value);
+
+	ccc = get_ccc_state(data, handle);
+	if (!ccc) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	ccc->value = ccc_value;
+
+	/* Send write response first */
+	gatt_db_attribute_write_result(attrib, id, 0);
+
+	/* If notifications/indications enabled on Real-time Ranging Data CCCD,
+	 * inject fake HCI CS subevent data to trigger notifications
+	 */
+	if (handle == 0x0006 && ccc_value != 0x0000 && data->rap) {
+		size_t event_size = sizeof(struct rap_ev_cs_subevent_result) +
+				    sizeof(struct cs_step_data);
+		struct rap_ev_cs_subevent_result *fake_event;
+		struct cs_mode_zero_data *mode_zero;
+
+		if (tester_use_debug())
+			tester_debug("Injecting fake CS data...");
+
+		fake_event = g_malloc0(event_size);
+		if (!fake_event)
+			return;  /* Already sent write response */
+
+		/* Fill in the header fields */
+		fake_event->conn_hdl = 0x0001;
+		fake_event->config_id = 0x01;
+		fake_event->start_acl_conn_evt_counter = 0x0000;
+		fake_event->proc_counter = 0x0001;
+		fake_event->freq_comp = 0x0000;
+		fake_event->ref_pwr_lvl = 0x00;
+		fake_event->proc_done_status = 0x00;
+		fake_event->subevt_done_status = 0x00;
+		fake_event->abort_reason = 0x00;
+		fake_event->num_ant_paths = 0x01;
+		fake_event->num_steps_reported = 0x01;
+
+		fake_event->step_data[0].step_mode = CS_MODE_ZERO;
+		fake_event->step_data[0].step_chnl = 0x00;
+		/* Mode 0: 1+1+1+4 bytes */
+		fake_event->step_data[0].step_data_length = 4;
+mode_zero = &fake_event->step_data[0].step_mode_data.mode_zero_data;
+		mode_zero->packet_quality = 0x01;
+		mode_zero->packet_rssi_dbm = 0x02;
+		mode_zero->packet_ant = 0x03;
+		mode_zero->init_measured_freq_offset = 0x04;
+
+		/* Inject the fake event to trigger notification */
+		bt_rap_hci_cs_subevent_result_callback(event_size, fake_event,
+						       data->rap);
+
+		g_free(fake_event);
+	}
+	return;
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
 static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					uint8_t opcode, struct bt_att *att,
@@ -184,10 +273,21 @@ done:
 
 static void ras_attached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data) {
+		data->rap = rap;
+		bt_rap_ref(rap);  /* Keep a reference */
+	}
 }
 
 static void ras_detached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data && data->rap == rap)
+		data->rap = NULL;
+
 	bt_rap_unref(rap);
 }
 
@@ -205,17 +305,18 @@ static void test_server(const void *user_data)
 	att = bt_att_new(io_get_fd(io), false);
 	g_assert(att);
 
+	bt_att_set_security(att, BT_ATT_SECURITY_MEDIUM);
 	bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL);
 
 	data->db = gatt_db_new();
 	g_assert(data->db);
 
-	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
+	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
 					gatt_notify_cb, data);
 
 	bt_rap_add_db(data->db);
 
-	data->ras_id = bt_rap_register(ras_attached, ras_detached, NULL);
+	data->ras_id = bt_rap_register(ras_attached, ras_detached, data);
 
 	data->server = bt_gatt_server_new(data->db, att, 64, 0);
 	g_assert(data->server);
@@ -426,6 +527,150 @@ static void test_server(const void *user_data)
 	DISC_RAS_CHAR_AFTER_TYPE, \
 	RAS_FIND_INFO
 
+/*
+ * RAS/SR/RCO/BV-01-C – Characteristic Read: RAS Features
+ *
+ *  ATT: Read Request (0x0a) len 2
+ *       Handle: 0x0003 (RAS Features value handle)
+ *
+ *  ATT: Read Response (0x0b) len 5
+ *       Value: 0x01 0x00 0x00 0x00
+ *       Feature bits:
+ *         Bit 0: Real-time ranging (1 = supported)
+ *         Bit 1: Retrieve stored results (0 = not supported)
+ *         Bit 2: Abort operation (0 = not supported)
+ *
+ *  Note: The RAS Features characteristic is registered with
+ *  BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT. Since the test sets
+ *  BT_ATT_SECURITY_MEDIUM, the encryption requirement is satisfied
+ *  and the server returns the feature value showing real-time ranging
+ *  support.
+ */
+
+#define ATT_READ_RAS_FEATURES \
+	IOV_DATA(0x0a, 0x03, 0x00), \
+	IOV_DATA(0x0b, 0x01, 0x00, 0x00, 0x00)
+
+#define RAS_SR_RCO_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	ATT_READ_RAS_FEATURES
+
+/*
+ * RAS Real-time Ranging Data CCCD Configuration
+ * Round 1: Enable/Disable notifications (CCCD = 0x0001)
+ * Round 2: Enable/Disable indications (CCCD = 0x0002)
+ */
+#define RAS_REALTIME_CCCD_CONFIG \
+	/* Round 1: Enable notifications on Real-time Ranging Data CCCD */ \
+	/* (handle 0x0006) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Round 2: Enable indications on Real-time Ranging Data CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x02, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable indications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Enable both notifications and indications (CCCD = 0x0003)
+ * Expect notification (0x1b) to be sent, not indication (0x1d)
+ * Then disable CCCD
+ *
+ * Note: This test is currently disabled because the GATT server rejects
+ * CCCD value 0x0003 (both notifications and indications enabled) before
+ * reaching the custom callback. The test infrastructure needs to be updated
+ * to support this scenario.
+ */
+#define RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE \
+	/* Enable notifications only (CCCD = 0x0001) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Disconnection/Reconnection simulation for Real-time Ranging Data
+ * Enable notifications, disable (disconnect), re-enable (reconnect), disable
+ */
+#define RAS_REALTIME_CCCD_DISCONNECT_RECONNECT \
+	/* Step 1: Enable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 3: Disable CCCD (simulates disconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 4: Re-enable notifications (simulates reconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD to clean up */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
+ * Verify that the IUT can configure CCCD for notifications/indications
+ * of the Real-time Ranging Data characteristic.
+ */
+#define RAS_SR_RRD_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_CONFIG
+
+/*
+ * RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
+ * Verify that the IUT only sends Real-time Ranging Data notifications when
+ * configured for both notifications and indications (CCCD = 0x0003).
+ *
+ * Test Procedure:
+ * 1. Write 0x0003 to Real-time Ranging Data CCCD (enable both
+ *    notifications and indications)
+ * 2. Trigger CS Subevent Data (via fake HCI event injection)
+ * 3. Verify IUT sends only notifications (0x1b), not indications (0x1d)
+ */
+#define RAS_SR_RRD_BV_03_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE
+
+/*
+ * RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
+ * Verify that the IUT does not resume sending Real-time Ranging Data
+ * notifications or indications after a disconnection occurs.
+ *
+ * Test Procedure:
+ * 1. Enable Real-time Ranging Data notifications
+ * 2. Trigger CS Subevent Data (IUT sends notifications)
+ * 3. Disable CCCD (simulates disconnection - CCCD resets to 0x0000)
+ * 4. Re-enable notifications (simulates reconnection and reconfiguration)
+ * 5. Verify IUT does not send old ranging data
+ *
+ * Note: In a unit test, we simulate disconnection by disabling and re-enabling
+ * the CCCD. The RAP implementation should clear any pending data when CCCD is
+ * disabled, ensuring no old data is sent after re-enabling.
+ */
+#define RAS_SR_RRD_BV_05_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_DISCONNECT_RECONNECT
+
 int main(int argc, char *argv[])
 {
 	tester_init(&argc, &argv);
@@ -441,6 +686,17 @@ int main(int argc, char *argv[])
 					RAS_SR_SGGIT_CHA_BV_03_C);
 	define_test_ras("RAS/SR/SGGIT/CHA/BV-04-C", test_server,
 					RAS_SR_SGGIT_CHA_BV_04_C);
+	/* RAS Read Characteristic Operations */
+	define_test_ras("RAS/SR/RCO/BV-01-C", test_server,
+					RAS_SR_RCO_BV_01_C);
+	/* RAS Real-time Ranging Data */
+	define_test_ras("RAS/SR/RRD/BV-01-C", test_server,
+					RAS_SR_RRD_BV_01_C);
+	/* RAS Real-time Ranging Data with CS injection */
+	define_test_ras("RAS/SR/RRD/BV-03-C", test_server,
+					RAS_SR_RRD_BV_03_C);
+	define_test_ras("RAS/SR/RRD/BV-05-C", test_server,
+					RAS_SR_RRD_BV_05_C);
 
 	return tester_run();
 }
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v3 3/3] profiles/ranging: Read cs_mode_one_data members as per spec
From: Prathibha Madugonde @ 2026-04-27 11:35 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427113544.1063560-1-prathm@qti.qualcomm.com>

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

Rearrage reading of cs_mode_one_data struct members as per spec.
---
 profiles/ranging/rap_hci.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
index a3d2df608..08ddc077c 100644
--- a/profiles/ranging/rap_hci.c
+++ b/profiles/ranging/rap_hci.c
@@ -632,19 +632,22 @@ static void parse_mode_one_data(struct iovec *iov,
 	}
 
 	DBG("CS Step mode 1");
-	util_iov_pull_u8(iov, &mode_data->packet_quality);
-	util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm);
-	util_iov_pull_u8(iov, &mode_data->packet_ant);
-	util_iov_pull_u8(iov, &mode_data->packet_nadm);
-
-	if (iov->iov_len >= 2) {
-		util_iov_pull_le16(iov, &time_val);
-		if (cs_role == CS_REFLECTOR)
-			mode_data->tod_toa_refl = time_val;
-		else
-			mode_data->toa_tod_init = time_val;
+	/* Parse fixed fields in specification order */
+	if (!util_iov_pull_u8(iov, &mode_data->packet_quality) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_nadm) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm) ||
+		!util_iov_pull_le16(iov, &time_val) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_ant)) {
+		DBG("Mode 1: failed to parse basic fields");
+		memset(mode_data, 0, sizeof(*mode_data));
+		return;
 	}
 
+	if (cs_role == CS_REFLECTOR)
+		mode_data->tod_toa_refl = time_val;
+	else
+		mode_data->toa_tod_init = time_val;
+
 	if ((cs_rtt_type == 0x01 || cs_rtt_type == 0x02) &&
 		iov->iov_len >= 6) {
 		int16_t i_val, q_val;
-- 
2.34.1


^ permalink raw reply related

* [bluez/bluez] f862ae: Add RAS packet formatting and notification support...
From: prathibhamadugonde @ 2026-04-27 11:45 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1086088
  Home:   https://github.com/bluez/bluez
  Commit: f862ae559fc39fc8d83cf7b0e44f134fbfd14309
      https://github.com/bluez/bluez/commit/f862ae559fc39fc8d83cf7b0e44f134fbfd14309
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M src/shared/rap.c
    M src/shared/rap.h

  Log Message:
  -----------
  Add RAS packet formatting and notification support for CS reflector

Implement complete RAS data pipeline:
 - Handle HCI CS subevent result/continuation events
 - Serialize ranging/subevent headers per spec
 - GATT notifications for real-time ranging data


  Commit: dc62cb919f83f3929aab189a0332e0dc8d4de9c5
      https://github.com/bluez/bluez/commit/dc62cb919f83f3929aab189a0332e0dc8d4de9c5
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M profiles/ranging/rap_hci.c

  Log Message:
  -----------
  profiles/ranging: Read cs_mode_one_data members as per spec

Rearrage reading of cs_mode_one_data struct members as per spec.


Compare: https://github.com/bluez/bluez/compare/f862ae559fc3%5E...dc62cb919f83

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

^ permalink raw reply

* RE: Add RAS Packet format and Notification support
From: bluez.test.bot @ 2026-04-27 11:53 UTC (permalink / raw)
  To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260427113544.1063560-2-prathm@qti.qualcomm.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      1.92 seconds
GitLint                       FAIL      0.64 seconds
BuildEll                      PASS      20.03 seconds
BluezMake                     FAIL      19.47 seconds
MakeCheck                     FAIL      42.81 seconds
MakeDistcheck                 PASS      242.09 seconds
CheckValgrind                 FAIL      16.07 seconds
CheckSmatch                   FAIL      24.06 seconds
bluezmakeextell               FAIL      13.17 seconds
IncrementalBuild              FAIL      20.14 seconds
ScanBuild                     FAIL      35.04 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v3,1/3] Add RAS packet formatting and notification support for CS reflector

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
1: T1 Title exceeds max length (82>80): "[BlueZ,v3,1/3] Add RAS packet formatting and notification support for CS reflector"
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:

src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7579: src/shared/libshared_glib_la-rap.lo] Error 1
make: *** [Makefile:10820: check] Error 2
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:10820: check] Error 2
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:

src/shared/crypto.c:271:21: warning: Variable length array is used.
src/shared/crypto.c:272:23: warning: Variable length array is used.
src/shared/gatt-helpers.c:764:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:842:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1335:31: warning: Variable length array is used.
src/shared/gatt-helpers.c:1366:23: warning: Variable length array is used.
src/shared/gatt-server.c:279:25: warning: Variable length array is used.
src/shared/gatt-server.c:622:25: warning: Variable length array is used.
src/shared/gatt-server.c:720:25: warning: Variable length array is used.
src/shared/bap.c:312:25: warning: array of flexible structures
src/shared/bap.c: note: in included file:
./src/shared/ascs.h:88:25: warning: array of flexible structures
src/shared/rap.c:1543:41: warning: mixing declarations and code
src/shared/rap.c:1662:41: warning: mixing declarations and code
src/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):
/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function'
/usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction'
/usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction'
/usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction'
src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:

src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:

src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
[BlueZ,v3,1/3] Add RAS packet formatting and notification support for CS reflector

src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:

src/shared/gatt-client.c:447:21: warning: Use of memory after it is freed
        gatt_db_unregister(op->client->db, op->db_id);
                           ^~~~~~~~~~
src/shared/gatt-client.c:692:2: warning: Use of memory after it is freed
        discovery_op_complete(op, false, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:992:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1098:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1292:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1357:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1632:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1637:2: warning: Use of memory after it is freed
        discover_all(op);
        ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1693:56: warning: Use of memory after it is freed
        notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
                                          ~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2146:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2154:8: warning: Use of memory after it is freed
                                                        discovery_op_ref(op),
                                                        ^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3332:2: warning: Use of memory after it is freed
        complete_write_long_op(req, success, 0, false);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3354:2: warning: Use of memory after it is freed
        request_unref(req);
        ^~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
src/shared/rap.c: In function ‘handle_local_subevent_result’:
src/shared/rap.c:1543:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1543 |      uint32_t pct2 =
      |      ^~~~~~~~
src/shared/rap.c:1662:6: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement]
 1662 |      uint32_t pct2 =
      |      ^~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
make: *** [Makefile:4172: all] Error 2


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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [Bug 221426] New: Intel AX211 Bluetooth hci0 hardware error 0x0c with HID/HOG failures on 6.19.14-zen
From: bugzilla-daemon @ 2026-04-27 16:32 UTC (permalink / raw)
  To: linux-bluetooth

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

            Bug ID: 221426
           Summary: Intel AX211 Bluetooth hci0 hardware error 0x0c with
                    HID/HOG failures on 6.19.14-zen
           Product: Drivers
           Version: 2.5
          Hardware: All
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P3
         Component: Bluetooth
          Assignee: linux-bluetooth@vger.kernel.org
          Reporter: cristi@ieee.org
        Regression: No

Created attachment 309973
  --> https://bugzilla.kernel.org/attachment.cgi?id=309973&action=edit
Log highlights

# AX211 Bluetooth HID instability report

## Summary
Intel AX211 Bluetooth on this system intermittently fails with kernel hardware
error 0x0c and repeated BlueZ HOG profile failures. User-facing symptom is that
BLE mouse and keyboard become difficult or impossible to connect while Chrome
is open; closing Chrome often allows reconnect.

This appears to be a kernel or controller stability problem, potentially
amplified by power management settings and high USB activity.

## Environment
- Distro: ArchLinux rolling
- Kernel: 6.19.14-zen1-1-zen
- Bluetooth controller: Intel AX211 Bluetooth (USB ID 8087:0033)
- Devices affected: Logitech M720 mouse, Logitech K850 keyboard (BLE HID)

## Reproduction
1. Boot system normally.
2. Ensure Bluetooth service is active.
3. Open Chrome and use system normally.
4. Attempt to connect or keep connected BLE keyboard and mouse.
5. Observe failures, reconnect loops, or failed writes.
6. Close Chrome and retry; connection usually succeeds.

## Expected behavior
BLE mouse and keyboard should remain stable regardless of Chrome state.

## Actual behavior
BLE HID profile operation intermittently fails; reconnect loops occur and
controller errors are logged.

## High signal evidence
From kernel log:

    Bluetooth: hci0: Hardware error 0x0c
    Bluetooth: hci0: Retrieving Intel exception info failed (-16)

From bluetooth service log:

    profiles/input/hog-lib.c:output_written_cb() Write output report failed:
Request attribute has encountered an unlikely error
    Wrong size of start discovery return parameters

Power-management related state on this host:

    /sys/module/btusb/parameters/enable_autosuspend=Y
    /sys/module/usbcore/parameters/autosuspend=2

## Attachments
- System snapshot: ax211-bt-system-2026-04-27.txt
- Log highlights: ax211-bt-logs-2026-04-27.txt

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* [Bug 221426] Intel AX211 Bluetooth hci0 hardware error 0x0c with HID/HOG failures on 6.19.14-zen
From: bugzilla-daemon @ 2026-04-27 16:32 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <bug-221426-62941@https.bugzilla.kernel.org/>

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

--- Comment #1 from Cristian Cocos (cristi@ieee.org) ---
Created attachment 309974
  --> https://bugzilla.kernel.org/attachment.cgi?id=309974&action=edit
System snapshot

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* [PATCH BlueZ v4 0/3] Add RAS Packet format and Notification support
From: Prathibha Madugonde @ 2026-04-27 17:00 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg

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

Changes in v4:
 Fixed declaration after statement in src/shared/rap.c

Changes in v3:
 Fix for cs_mode_one_data struct members rearrage as per spec
 Reading of cs_mode_one_data in rap_hci.c
 Address cs_mode data struct padding in src/shared/rap.c 

Changes in v2:
 Fixed missing declaration in src/shared/rap.c

Patch overview:
  1/3 src/shared: Add RAS packet format and sending notifications to client
  2/3 uint/test-rap : Add PTS tests for CS reflector
  3/3 profiles/ranging: Read cs_mode_one_data members as per spec

 profiles/ranging/rap_hci.c |   25 +-
 src/shared/rap.c           | 1376 +++++++++++++++++++++++++++++++++++-
 src/shared/rap.h           |    4 +-
 unit/test-rap.c            |  264 ++++++-
 4 files changed, 1640 insertions(+), 29 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH BlueZ v4 1/3] src/shared: Add RAS packet formatting and notification support for CS reflector
From: Prathibha Madugonde @ 2026-04-27 17:00 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427170007.1282289-1-prathm@qti.qualcomm.com>

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

Implement complete RAS data pipeline:
 - Handle HCI CS subevent result/continuation events
 - Serialize ranging/subevent headers per spec
 - GATT notifications for real-time ranging data
---
 src/shared/rap.c | 1376 +++++++++++++++++++++++++++++++++++++++++++++-
 src/shared/rap.h |    4 +-
 2 files changed, 1366 insertions(+), 14 deletions(-)

diff --git a/src/shared/rap.c b/src/shared/rap.c
index ac6de04e0..db924b0e9 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -33,6 +33,170 @@
 /* Total number of attribute handles reserved for the RAS service */
 #define RAS_TOTAL_NUM_HANDLES		18
 
+/* 2(rc+cfg) + 1(tx_pwr) + 1(4 bits antenna_mask, 2 bits reserved,
+ * 2 bits pct_format)
+ */
+#define RAS_RANGING_HEADER_SIZE 4
+#define TOTAL_RAS_RANGING_HEADER_SIZE 5
+#define ATT_OVERHEAD 3 /* 1(opcode) + 2(char handle) */
+#define RAS_STEP_ABORTED_BIT   0x80/* set step aborted */
+#define RAS_SUBEVENT_HEADER_SIZE 8
+
+enum pct_format {
+	IQ = 0,
+	PHASE = 1,
+};
+
+enum ranging_done_status {
+	RANGING_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	RANGING_DONE_PARTIAL_RESULTS = 0x1,
+	RANGING_DONE_ABORTED = 0xF,
+};
+
+enum subevent_done_status {
+	SUBEVENT_DONE_ALL_RESULTS_COMPLETE = 0x0,
+	SUBEVENT_DONE_PARTIAL_RESULTS = 0x1,
+	SUBEVENT_DONE_ABORTED = 0xF,
+};
+
+enum ranging_abort_reason {
+	RANGING_ABORT_NO_ABORT = 0x0,
+	RANGING_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	RANGING_ABORT_INSUFFICIENT_FILTERED_CHANNELS = 0x2,
+	RANGING_ABORT_INSTANT_HAS_PASSED = 0x3,
+	RANGING_ABORT_UNSPECIFIED = 0xF,
+};
+
+enum subevent_abort_reason {
+	SUBEVENT_ABORT_NO_ABORT = 0x0,
+	SUBEVENT_ABORT_LOCAL_HOST_OR_REMOTE = 0x1,
+	SUBEVENT_ABORT_NO_CS_SYNC_RECEIVED = 0x2,
+	SUBEVENT_ABORT_SCHEDULING_CONFLICTS_OR_LIMITED_RESOURCES = 0x3,
+	SUBEVENT_ABORT_UNSPECIFIED = 0xF,
+};
+
+/* Segmentation header: 1 byte
+ * bit 0: first_segment
+ * bit 1: last_segment
+ * bits 2-7: rolling_segment_counter (6 bits)
+ */
+struct segmentation_header {
+	uint8_t first_segment;
+	uint8_t last_segment;
+	uint8_t rolling_segment_counter;
+};
+
+/* Macros to pack/unpack segmentation header */
+#define SEG_HDR_PACK(first, last, counter) \
+	((uint8_t)(((first) ? 0x01 : 0x00) | \
+		((last) ? 0x02 : 0x00) | \
+		(((counter) & 0x3F) << 2)))
+
+#define SEG_HDR_UNPACK_FIRST(byte)    ((byte) & 0x01)
+#define SEG_HDR_UNPACK_LAST(byte)     (((byte) >> 1) & 0x01)
+#define SEG_HDR_UNPACK_COUNTER(byte)  (((byte) >> 2) & 0x3F)
+
+/* Ranging header: 4 bytes
+ * 0-1: ranging_counter (12 bits) | configuration_id (4 bits)
+ *      [little-endian]
+ * byte 2: selected_tx_power (signed)
+ * byte 3: antenna_paths_mask (4 bits) | reserved (2 bits) |
+ *         pct_format (2 bits)
+ */
+struct ranging_header {
+	uint16_t ranging_counter;
+	uint8_t configuration_id;
+	int8_t selected_tx_power;
+	uint8_t antenna_paths_mask;
+	uint8_t pct_format;
+} __packed;
+
+/* Macros to pack/unpack ranging header */
+#define RANGING_HDR_PACK_BYTE0_1(rc, cfg) \
+	((uint16_t)(((rc) & 0x0FFF) | (((cfg) & 0x0F) << 12)))
+
+#define RANGING_HDR_PACK_BYTE3(ant_mask, pct_fmt) \
+	((uint8_t)(((ant_mask) & 0x0F) | (((pct_fmt) & 0x03) << 6)))
+
+#define RANGING_HDR_UNPACK_RC(byte0_1) \
+	((uint16_t)((byte0_1) & 0x0FFF))
+
+#define RANGING_HDR_UNPACK_CFG(byte0_1) \
+	((uint8_t)(((byte0_1) >> 12) & 0x0F))
+
+#define RANGING_HDR_UNPACK_ANT_MASK(byte3) \
+	((uint8_t)((byte3) & 0x0F))
+
+#define RANGING_HDR_UNPACK_PCT_FMT(byte3) \
+	((uint8_t)(((byte3) >> 6) & 0x03))
+
+struct ras_subevent_header {
+	uint16_t start_acl_conn_event;
+	uint16_t frequency_compensation;
+	uint8_t ranging_done_status;
+	uint8_t subevent_done_status;
+	uint8_t ranging_abort_reason;
+	uint8_t subevent_abort_reason;
+	int8_t reference_power_level;
+	uint8_t num_steps_reported;
+};
+
+struct ras_subevent {
+	struct ras_subevent_header subevent_header;
+	uint8_t subevent_data[];
+};
+
+/* Role maps to Core CS roles (initiator/reflector) */
+enum cs_role {
+	CS_ROLE_INITIATOR = 0x00,
+	CS_ROLE_REFLECTOR = 0x01,
+};
+
+#define CS_INVALID_CONFIG_ID   0xFF
+/* 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
+};
+
+/* Main cs_procedure_data  */
+struct cs_procedure_data {
+	/* Identity and counters */
+	uint16_t counter;
+	uint8_t  num_antenna_paths;
+	/* Flags and status */
+	enum cs_procedure_done_status local_status;
+	enum cs_procedure_done_status remote_status;
+	bool contains_complete_subevent_;
+	/* RAS aggregation */
+	struct segmentation_header segmentation_header_;
+	struct ranging_header      ranging_header_;
+	struct iovec       ras_raw_data_;        /* raw concatenated */
+	uint16_t           ras_raw_data_index_;
+	struct ras_subevent_header  ras_subevent_header_;
+	struct iovec       ras_subevent_data_;   /* buffer per subevent */
+	uint8_t            ras_subevent_counter_;
+	/* Reference power levels */
+	int8_t initiator_reference_power_level;
+	int8_t reflector_reference_power_level;
+	bool ranging_header_prepended_;
+	bool ras_subevent_header_emitted;
+};
+
+struct cstracker {
+	enum cs_role        role;                 /* INITIATOR/REFLECTOR */
+	uint8_t             config_id;            /* CS_INVALID_CONFIG_ID */
+	uint8_t             selected_tx_power;    /* PROC_ENABLE_COMPLETE */
+	uint8_t             rtt_type;             /* RTT type */
+	struct cs_procedure_data *current_proc;
+	/* Cached header values for CONT events (per-connection state) */
+	uint16_t last_proc_counter;
+	uint16_t last_start_acl_conn_evt_counter;
+	uint16_t last_freq_comp;
+	int8_t last_ref_pwr_lvl;
+};
+
 /* Ranging Service context */
 struct ras {
 	struct bt_rap_db *rapdb;
@@ -43,9 +207,17 @@ struct ras {
 	struct gatt_db_attribute *realtime_chrc;
 	struct gatt_db_attribute *realtime_chrc_ccc;
 	struct gatt_db_attribute *ondemand_chrc;
+	struct gatt_db_attribute *ondemand_ccc;
 	struct gatt_db_attribute *cp_chrc;
+	struct gatt_db_attribute *cp_ccc;
 	struct gatt_db_attribute *ready_chrc;
+	struct gatt_db_attribute *ready_ccc;
 	struct gatt_db_attribute *overwritten_chrc;
+	struct gatt_db_attribute *overwritten_ccc;
+
+	/* CCC state tracking for mutual exclusivity */
+	uint16_t realtime_ccc_value;
+	uint16_t ondemand_ccc_value;
 };
 
 struct bt_rap_db {
@@ -70,6 +242,7 @@ struct bt_rap {
 	bt_rap_destroy_func_t debug_destroy;
 	void *debug_data;
 	void *user_data;
+	struct cstracker *resptracker;
 };
 
 static struct queue *rap_db;
@@ -90,6 +263,209 @@ struct bt_rap_ready {
 	void *data;
 };
 
+uint16_t default_ras_mtu = 247; /*Section 3.1.2 of RAP 1.0*/
+uint8_t ras_segment_header_size = 1;
+
+static struct cs_procedure_data *cs_procedure_data_create(
+					uint16_t procedure_counter,
+					uint8_t num_antenna_paths,
+					uint8_t configuration_id,
+					uint8_t selected_tx_power)
+{
+	struct cs_procedure_data *d;
+	uint8_t i;
+
+	d = calloc(1, sizeof(struct cs_procedure_data));
+
+	if (!d)
+		return NULL;
+
+	d->counter = procedure_counter;
+	d->num_antenna_paths = num_antenna_paths;
+	d->local_status = CS_PROC_PARTIAL_RESULTS;
+	d->remote_status = CS_PROC_PARTIAL_RESULTS;
+	d->contains_complete_subevent_ = false;
+	d->segmentation_header_.first_segment = 1;
+	d->segmentation_header_.last_segment = 0;
+	d->segmentation_header_.rolling_segment_counter = 0;
+	d->ranging_header_.ranging_counter = procedure_counter;
+	d->ranging_header_.configuration_id = configuration_id;
+	d->ranging_header_.selected_tx_power = selected_tx_power;
+	d->ranging_header_.antenna_paths_mask = 0;
+
+	for (i = 0; i < num_antenna_paths; i++)
+		d->ranging_header_.antenna_paths_mask |= (1u << i);
+
+	d->ranging_header_.pct_format = IQ;
+	memset(&d->ras_raw_data_, 0, sizeof(d->ras_raw_data_));
+	d->ras_raw_data_index_ = 0;
+	memset(&d->ras_subevent_data_, 0, sizeof(d->ras_subevent_data_));
+	d->ras_subevent_counter_ = 0;
+	d->initiator_reference_power_level = 0;
+	d->reflector_reference_power_level = 0;
+	d->ranging_header_prepended_ = false;
+	d->ras_subevent_header_emitted = false;
+
+	return d;
+}
+
+static void cs_procedure_data_destroy(struct cs_procedure_data *d)
+{
+	if (!d)
+		return;
+
+	free(d->ras_raw_data_.iov_base);
+	free(d->ras_subevent_data_.iov_base);
+	free(d);
+}
+
+static void cs_pd_set_local_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->local_status = s;
+}
+
+static void cs_pd_set_remote_status(struct cs_procedure_data *d,
+				enum cs_procedure_done_status s)
+{
+	if (d)
+		d->remote_status = s;
+}
+
+static void cs_pd_set_reference_power_levels(struct cs_procedure_data *d,
+					int8_t init_lvl, int8_t ref_lvl)
+{
+	if (!d)
+		return;
+
+	d->initiator_reference_power_level = init_lvl;
+	d->reflector_reference_power_level = ref_lvl;
+}
+
+static void cs_pd_ras_begin_subevent(struct cs_procedure_data *d,
+				uint16_t start_acl_conn_event,
+				uint16_t frequency_compensation,
+				int8_t reference_power_level)
+{
+	if (!d)
+		return;
+
+	d->ras_subevent_counter_++;
+	d->ras_subevent_header_.start_acl_conn_event = start_acl_conn_event;
+	d->ras_subevent_header_.frequency_compensation =
+		frequency_compensation;
+	d->ras_subevent_header_.reference_power_level = reference_power_level;
+	d->ras_subevent_header_.num_steps_reported = 0;
+	d->ras_subevent_header_emitted = false;
+	d->ras_subevent_data_.iov_len = 0;
+}
+
+static bool cs_pd_ras_append_subevent_bytes(struct cs_procedure_data *d,
+					const uint8_t *bytes, size_t len)
+{
+	if (!d || !bytes || len == 0)
+		return false;
+
+	return util_iov_append(&d->ras_subevent_data_, bytes, len) != NULL;
+}
+
+static inline size_t serialize_ras_subevent_header(
+				const struct ras_subevent_header *h,
+				uint8_t *out, size_t out_len)
+{
+	uint16_t start_le;
+	uint16_t freq_le;
+
+	if (!h || !out || out_len < RAS_SUBEVENT_HEADER_SIZE)
+		return 0;
+
+	start_le = (uint16_t)h->start_acl_conn_event;
+	out[0] = (uint8_t)(start_le & 0xFF);
+	out[1] = (uint8_t)((start_le >> 8) & 0xFF);
+
+	freq_le = (uint16_t)h->frequency_compensation;
+	out[2] = (uint8_t)(freq_le & 0xFF);
+	out[3] = (uint8_t)((freq_le >> 8) & 0xFF);
+
+	out[4] = (uint8_t)((h->ranging_done_status & 0x0F) |
+			((h->subevent_done_status & 0x0F) << 4));
+
+	out[5] = (uint8_t)((h->ranging_abort_reason & 0x0F) |
+			((h->subevent_abort_reason & 0x0F) << 4));
+
+	out[6] = (uint8_t)h->reference_power_level;
+	out[7] = (uint8_t)h->num_steps_reported;
+
+	return RAS_SUBEVENT_HEADER_SIZE;
+}
+
+static bool cs_pd_ras_commit_subevent(struct cs_procedure_data *d,
+				uint8_t num_steps_reported,
+				uint8_t ranging_done_status,
+				uint8_t subevent_done_status,
+				uint8_t ranging_abort_reason,
+				uint8_t subevent_abort_reason)
+{
+	size_t hdr_sz;
+	size_t payload_sz;
+	size_t total;
+	uint8_t *buf;
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	d->ras_subevent_header_.num_steps_reported =
+		(uint8_t)(d->ras_subevent_header_.num_steps_reported +
+			num_steps_reported);
+	d->ras_subevent_header_.ranging_done_status = ranging_done_status;
+	d->ras_subevent_header_.subevent_done_status = subevent_done_status;
+	d->ras_subevent_header_.ranging_abort_reason = ranging_abort_reason;
+	d->ras_subevent_header_.subevent_abort_reason = subevent_abort_reason;
+
+	if (subevent_done_status == SUBEVENT_DONE_ALL_RESULTS_COMPLETE)
+		d->contains_complete_subevent_ = true;
+
+	if (subevent_done_status == SUBEVENT_DONE_PARTIAL_RESULTS)
+		return true;
+
+	if (!d->ras_subevent_header_emitted) {
+		hdr_sz = RAS_SUBEVENT_HEADER_SIZE;
+		payload_sz = d->ras_subevent_data_.iov_len;
+		total = hdr_sz + payload_sz;
+		buf = (uint8_t *)malloc(total);
+
+		if (!buf)
+			return false;
+
+		w = serialize_ras_subevent_header(&d->ras_subevent_header_,
+						buf, total);
+
+		if (w != hdr_sz) {
+			free(buf);
+			return false;
+		}
+
+		if (payload_sz > 0)
+			memcpy(buf + hdr_sz,
+				(const uint8_t *)d->ras_subevent_data_.iov_base,
+				payload_sz);
+
+		ok = util_iov_append(&d->ras_raw_data_, buf, total) != NULL;
+		free(buf);
+
+		if (!ok)
+			return false;
+
+		d->ras_subevent_data_.iov_len = 0;
+		d->ras_subevent_header_emitted = true;
+	}
+
+	return true;
+}
+
 static struct ras *rap_get_ras(struct bt_rap *rap)
 {
 	if (!rap)
@@ -155,6 +531,11 @@ static void rap_free(void *data)
 
 	rap_db_free(rap->rrapdb);
 
+	if (rap->resptracker) {
+		free(rap->resptracker);
+		rap->resptracker = NULL;
+	}
+
 	queue_destroy(rap->notify, free);
 	queue_destroy(rap->pending, NULL);
 	queue_destroy(rap->ready_cbs, rap_ready_free);
@@ -240,6 +621,22 @@ bool bt_rap_set_debug(struct bt_rap *rap, bt_rap_debug_func_t func,
 	return true;
 }
 
+static void cs_tracker_init(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	memset(t, 0, sizeof(*t));
+	t->role = CS_ROLE_REFLECTOR;
+	t->config_id = CS_INVALID_CONFIG_ID;
+	t->rtt_type = 0;
+	t->selected_tx_power = 0;
+	t->last_proc_counter = 0;
+	t->last_start_acl_conn_evt_counter = 0;
+	t->last_freq_comp = 0;
+	t->last_ref_pwr_lvl = 0;
+}
+
 static void ras_features_read_cb(struct gatt_db_attribute *attrib,
 				 unsigned int id, uint16_t offset,
 				 uint8_t opcode, struct bt_att *att,
@@ -304,6 +701,225 @@ static void ras_data_overwritten_read_cb(struct gatt_db_attribute *attrib,
 	gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
 }
 
+/* CCC write callbacks for custom handling */
+static void ras_realtime_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable realtime
+	 * while ondemand is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->ondemand_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->realtime_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ondemand_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+				BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 && ccc_value != 0x0002 &&
+	    ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	/* Check mutual exclusivity: reject if trying to enable ondemand
+	 * while realtime is already enabled
+	 */
+	if (ccc_value != 0x0000 && ras->realtime_ccc_value != 0x0000) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_CCC_IMPROPERLY_CONFIGURED);
+		return;
+	}
+
+	/* Update state */
+	ras->ondemand_ccc_value = ccc_value;
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_cp_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_ready_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void ras_overwritten_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct ras *ras = user_data;
+	uint16_t ccc_value;
+
+	if (!ras) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY);
+		return;
+	}
+
+	if (offset) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ATT_ERROR_INVALID_OFFSET);
+		return;
+	}
+
+	if (len != 2) {
+		gatt_db_attribute_write_result(attrib, id,
+			BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN);
+		return;
+	}
+
+	ccc_value = get_le16(value);
+
+	if (ccc_value != 0x0000 && ccc_value != 0x0001 &&
+	    ccc_value != 0x0002 && ccc_value != 0x0003) {
+		gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		return;
+	}
+
+	gatt_db_attribute_write_result(attrib, id, 0);
+}
 /* Service registration – store attribute pointers */
 static struct ras *register_ras_service(struct gatt_db *db)
 {
@@ -349,9 +965,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  NULL, NULL, ras);
 
 	ras->realtime_chrc_ccc =
-		gatt_db_service_add_ccc(ras->svc,
-					BT_ATT_PERM_READ |
-					BT_ATT_PERM_WRITE);
+		gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_realtime_ccc_write_cb, ras);
 
 	/* On-demand Ranging Data */
 	bt_uuid16_create(&uuid, RAS_ONDEMAND_DATA_UUID);
@@ -364,8 +980,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_ondemand_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ondemand_ccc_write_cb, ras);
 
 	/* RAS Control Point */
 	bt_uuid16_create(&uuid, RAS_CONTROL_POINT_UUID);
@@ -379,8 +996,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_control_point_write_cb,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_cp_ccc_write_cb, ras);
 
 	/* RAS Data Ready */
 	bt_uuid16_create(&uuid, RAS_DATA_READY_UUID);
@@ -394,8 +1012,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_ready_read_cb, NULL,
 						  ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_ready_ccc_write_cb, ras);
 
 	/* RAS Data Overwritten */
 	bt_uuid16_create(&uuid, RAS_DATA_OVERWRITTEN_UUID);
@@ -409,8 +1028,9 @@ static struct ras *register_ras_service(struct gatt_db *db)
 						  ras_data_overwritten_read_cb,
 						  NULL, ras);
 
-	gatt_db_service_add_ccc(ras->svc,
-				BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+	ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+					ras_overwritten_ccc_write_cb, ras);
 
 	/* Activate the service */
 	gatt_db_service_set_active(ras->svc, true);
@@ -503,32 +1123,746 @@ bool bt_rap_unregister(unsigned int id)
 	return true;
 }
 
+static inline size_t serialize_segmentation_header(
+				const struct segmentation_header *s,
+				uint8_t *out, size_t out_len)
+{
+	if (!s || !out || out_len < 1)
+		return 0;
+
+	/* [0] bit0: first, bit1: last, bits2..7: rolling counter */
+	out[0] = (s->first_segment ? 0x01 : 0x00) |
+		(s->last_segment ? 0x02 : 0x00) |
+		((s->rolling_segment_counter & 0x3F) << 2);
+
+	return 1;
+}
+
+static inline size_t serialize_ranging_header(const struct ranging_header *r,
+						uint8_t *out, size_t out_len)
+{
+	uint16_t rcid;
+
+	if (!r || !out || out_len < RAS_RANGING_HEADER_SIZE)
+		return 0;
+
+	/* Little-endian pack: [rc (12 bits) | cfg_id (4 bits)] */
+	rcid = RANGING_HDR_PACK_BYTE0_1(r->ranging_counter,
+					r->configuration_id);
+
+	put_le16(rcid, out + 0);
+	out[2] = (uint8_t)r->selected_tx_power;
+	/* Byte 3: antenna_paths_mask | reserved | pct_format */
+	out[3] = RANGING_HDR_PACK_BYTE3(r->antenna_paths_mask,
+					r->pct_format);
+
+	return RAS_RANGING_HEADER_SIZE;
+}
+
+static inline uint16_t ras_att_value_payload_max(struct bt_rap *rap)
+{
+	struct bt_att *att = bt_rap_get_att(rap);
+	uint16_t mtu = att ? bt_att_get_mtu(att) : default_ras_mtu;
+
+	return (uint16_t)(mtu > ATT_OVERHEAD ?
+		(mtu - ATT_OVERHEAD - TOTAL_RAS_RANGING_HEADER_SIZE -
+		ras_segment_header_size) : 0);
+}
+
+/* Prepend data to an iovec */
+static bool iov_prepend_bytes(struct iovec *iov, const uint8_t *bytes,
+				size_t len)
+{
+	size_t new_len;
+	void *new_base;
+
+	if (!iov || !bytes || len == 0)
+		return false;
+
+	new_len = iov->iov_len + len;
+	new_base = malloc(new_len);
+
+	if (!new_base)
+		return false;
+
+	memcpy(new_base, bytes, len);
+
+	if (iov->iov_len > 0)
+		memcpy((uint8_t *)new_base + len, iov->iov_base,
+			iov->iov_len);
+
+	free(iov->iov_base);
+	iov->iov_base = new_base;
+	iov->iov_len = new_len;
+
+	return true;
+}
+
+/* Append the 4-byte RangingHeader to ras_raw_data_ on first segment */
+static bool ras_maybe_prepend_ranging_header(struct cs_procedure_data *d)
+{
+	uint8_t hdr[RAS_RANGING_HEADER_SIZE];
+	size_t w;
+	bool ok;
+
+	if (!d)
+		return false;
+
+	if (d->ranging_header_prepended_)
+		return false;
+
+	if (!d->segmentation_header_.first_segment)
+		return false;
+
+	if (d->ras_raw_data_index_ != 0)
+		return false;
+
+	w = serialize_ranging_header(&d->ranging_header_, hdr, sizeof(hdr));
+
+	if (w != RAS_RANGING_HEADER_SIZE)
+		return false;
+
+	ok = iov_prepend_bytes(&d->ras_raw_data_, hdr, w);
+
+	if (ok)
+		d->ranging_header_prepended_ = true;
+
+	return ok;
+}
+
+static void send_ras_segment_data(struct bt_rap *rap,
+				struct cs_procedure_data *proc)
+{
+	struct ras *ras;
+	uint16_t value_max;
+	const uint16_t header_len = ras_segment_header_size;
+	uint16_t raw_payload_size;
+	bool ok;
+
+	if (!rap || !proc)
+		return;
+
+	if (!rap->lrapdb || !rap->lrapdb->ras)
+		return;
+	ras = rap->lrapdb->ras;
+	value_max = ras_att_value_payload_max(rap);
+
+	if (value_max == 0) {
+		DBG(rap, "value_max=0 (MTU not available?)");
+		return;
+	}
+
+	if (value_max <= header_len) {
+		DBG(rap, "value_max(%u) too small for header", value_max);
+		return;
+	}
+
+	raw_payload_size = (uint16_t)(value_max - header_len);
+
+	/* Convert tail recursion to loop */
+	while (true) {
+		size_t total_len = proc->ras_raw_data_.iov_len;
+		size_t index = proc->ras_raw_data_index_;
+		size_t unsent_data_size;
+		uint16_t copy_size;
+		uint16_t seg_len;
+		uint8_t *seg;
+		uint16_t wr = 0;
+
+		if (index > total_len)
+			index = total_len;
+
+		unsent_data_size = total_len - index;
+
+		if (unsent_data_size == 0)
+			return;
+
+		/* Set last_segment if procedure complete or fits in segment */
+		if ((proc->local_status != CS_PROC_PARTIAL_RESULTS &&
+				unsent_data_size <= raw_payload_size) ||
+			(proc->contains_complete_subevent_ &&
+				unsent_data_size <= raw_payload_size)) {
+			proc->segmentation_header_.last_segment = 1;
+		} else {
+			proc->segmentation_header_.last_segment = 0;
+		}
+
+		/* Wait for more data if needed and not last segment */
+		if (unsent_data_size < raw_payload_size &&
+				proc->segmentation_header_.last_segment == 0) {
+			DBG(rap, "waiting for more data (unsent=%zu < "
+				"payload=%u)", unsent_data_size,
+				raw_payload_size);
+			return;
+		}
+
+		copy_size = (uint16_t)((unsent_data_size < raw_payload_size) ?
+				unsent_data_size : raw_payload_size);
+		seg_len = (uint16_t)(header_len + copy_size);
+		seg = (uint8_t *)malloc(seg_len);
+
+		if (!seg) {
+			DBG(rap, "OOM (%u)", seg_len);
+			return;
+		}
+
+		wr += (uint16_t)serialize_segmentation_header(
+				&proc->segmentation_header_, seg + wr,
+				seg_len - wr);
+		memcpy(seg + wr,
+			(const uint8_t *)proc->ras_raw_data_.iov_base + index,
+			copy_size);
+		wr += copy_size;
+
+		/* Try sending to real-time characteristic */
+		if (ras->realtime_chrc)
+			ok = gatt_db_attribute_notify(ras->realtime_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		/* Try sending to on-demand characteristic */
+		if (ras->ondemand_chrc)
+			ok = gatt_db_attribute_notify(ras->ondemand_chrc, seg,
+						wr, bt_rap_get_att(rap));
+
+		free(seg);
+
+		if (!ok) {
+			DBG(rap, "Failed to send RAS notification");
+			return;
+		}
+
+		/* Advance read cursor and update segmentation state */
+		proc->ras_raw_data_index_ += copy_size;
+		proc->segmentation_header_.first_segment = 0;
+		proc->segmentation_header_.rolling_segment_counter =
+			(uint8_t)((proc->segmentation_header_
+				.rolling_segment_counter + 1) & 0x3F);
+
+		if (proc->segmentation_header_.last_segment ||
+				proc->ras_raw_data_index_ >=
+				proc->ras_raw_data_.iov_len) {
+			DBG(rap, "RAS clear ras buffers");
+			proc->ras_raw_data_.iov_len = 0;
+			proc->ras_raw_data_index_ = 0;
+			proc->ranging_header_prepended_ = false;
+			return;
+		}
+	}
+}
+
+static inline void resptracker_reset_current_proc(struct cstracker *t)
+{
+	if (!t)
+		return;
+
+	if (t->current_proc) {
+		cs_procedure_data_destroy(t->current_proc);
+		t->current_proc = NULL;
+	}
+}
+/* Unified local subevent handler */
+static void handle_local_subevent_result(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 void *step_bytes)
+{
+	struct cstracker *resptracker;
+	struct cs_procedure_data *proc;
+	const struct cs_step_data *steps;
+	uint8_t idx;
+
+	if (!rap || !rap->resptracker || !step_bytes)
+		return;
+
+	resptracker = rap->resptracker;
+
+	if (resptracker->current_proc) {
+		struct cs_procedure_data *cur = resptracker->current_proc;
+
+		if (has_header_fields && cur->counter != proc_counter) {
+			/* Safety: a new procedure; destroy the previous one */
+			resptracker_reset_current_proc(resptracker);
+		}
+	}
+
+	proc = resptracker->current_proc;
+	/* Cache header info from a RESULT event for later CONT usage */
+	if (has_header_fields) {
+		resptracker->last_proc_counter = proc_counter;
+		resptracker->last_start_acl_conn_evt_counter =
+			start_acl_conn_evt_counter;
+		resptracker->last_freq_comp = freq_comp;
+		resptracker->last_ref_pwr_lvl = ref_pwr_lvl;
+	}
+
+	/* Create the procedure on first use */
+	if (!proc) {
+		uint16_t create_counter = has_header_fields ? proc_counter :
+					resptracker->last_proc_counter;
+
+		proc = cs_procedure_data_create(create_counter,
+						num_ant_paths,
+						config_id,
+						resptracker->selected_tx_power);
+		if (!proc)
+			return;
+
+		resptracker->current_proc = proc;
+
+		/* Reference power levels and status defaults */
+		cs_pd_set_reference_power_levels(proc,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl,
+			has_header_fields ? ref_pwr_lvl :
+				resptracker->last_ref_pwr_lvl);
+		cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+		cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+	}
+
+	/* Begin a new RAS subevent only when we have header fields */
+	if (has_header_fields) {
+		cs_pd_ras_begin_subevent(proc,
+					start_acl_conn_evt_counter,
+					freq_comp,
+					ref_pwr_lvl);
+	}
+
+	/* step_bytes points to an array of struct cs_step_data */
+	steps = (const struct cs_step_data *)step_bytes;
+
+	/* Process each step */
+	for (idx = 0; idx < num_steps_reported; idx++) {
+		const struct cs_step_data *step = &steps[idx];
+		const uint8_t mode = step->step_mode;
+		const uint8_t channel = step->step_chnl;
+		const uint8_t payload_len = step->step_data_length;
+		uint8_t mode_byte;
+		uint8_t mode_type;
+		const uint8_t *payload;
+		uint8_t plen;
+		bool step_aborted;
+
+		/* Check if step is aborted: bit 7 of step_mode or
+		 * 0 payload len
+		 */
+		step_aborted = (mode & RAS_STEP_ABORTED_BIT) ||
+			(payload_len == 0);
+
+		DBG(rap, "step[%u]: mode=0x%02x channel=%u payload_len=%u "
+			"aborted=%s", idx, mode, channel, payload_len,
+			step_aborted ? "YES" : "NO");
+
+		/* Slim-step serialization for RAS:
+		 * - 1 byte: mode (bit7 set if aborted)
+		 * - payload: exactly step_data_length bytes (raw mode data)
+		 */
+		mode_byte = step->step_mode;
+		payload = (const uint8_t *)&step->step_mode_data;
+		plen = step->step_data_length;
+
+		if (step_aborted) {
+			/* Ensure abort bit is set */
+			mode_byte |= RAS_STEP_ABORTED_BIT;
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+			/* No payload when aborted - per RAS spec Table 3.8 */
+			DBG(rap, "step[%u]: mode=0x%02x aborted, "
+				"no payload sent", idx, mode_byte);
+		} else {
+			mode_type = mode & 0x03;
+
+			/* Mode byte first (without abort bit) */
+			cs_pd_ras_append_subevent_bytes(proc, &mode_byte, 1);
+
+			switch (mode_type) {
+			case CS_MODE_ZERO: {
+				/* Mode 0: use raw structure bytes */
+				payload =
+					(const uint8_t *)&step->step_mode_data;
+				plen = step->step_data_length;
+				cs_pd_ras_append_subevent_bytes(proc,
+								payload, plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode0 "
+					"payload_len=%u sent",
+					idx, mode_byte, (unsigned int)plen);
+				break;
+			}
+			case CS_MODE_ONE: {
+				const struct cs_mode_one_data *m1 =
+					&step->step_mode_data.mode_one_data;
+				uint8_t buf[16];  /* Max Mode 1 size */
+				uint8_t *p = buf;
+				uint16_t time_val;
+				uint32_t pct1;
+				uint32_t pct2;
+				enum cs_role cs_role = resptracker->role;
+				uint8_t cs_rtt_type = resptracker->rtt_type;
+
+				*p++ = m1->packet_quality;
+				*p++ = m1->packet_nadm;
+				*p++ = m1->packet_rssi_dbm;
+
+				/* Time value (2 bytes LE) - use the
+				 * appropriate field based on role
+				 */
+				if (cs_role == CS_ROLE_REFLECTOR)
+					time_val = m1->tod_toa_refl;
+				else
+					time_val = m1->toa_tod_init;
+
+				*p++ = (uint8_t)(time_val & 0xFF);
+				*p++ = (uint8_t)((time_val >> 8) & 0xFF);
+
+				*p++ = m1->packet_ant;
+
+				/* PCT samples if RTT type contains
+				 * sounding sequence
+				 */
+				if (cs_rtt_type == 0x01 ||
+						cs_rtt_type == 0x02) {
+					pct1 = ((uint32_t)(m1->packet_pct1
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct1
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct1 & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 16) & 0xFF);
+
+					/* PCT2 (3 bytes) */
+					pct2 = ((uint32_t)(m1->packet_pct2
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct2
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct2 & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 16) & 0xFF);
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode1 "
+					"serialized payload_len=%u role=%s "
+					"rtt_type=0x%02x",
+					idx, mode_byte, (unsigned int)plen,
+					cs_role == CS_ROLE_INITIATOR ?
+						"INIT" : "REFL", cs_rtt_type);
+				break;
+			}
+			case CS_MODE_TWO: {
+				const struct cs_mode_two_data *m2 =
+					&step->step_mode_data.mode_two_data;
+				/* Max Mode 2 size: 1 + 5*(3+1) = 21 bytes */
+				uint8_t buf[64];
+				uint8_t *p = buf;
+				uint8_t k;
+				uint8_t num_paths = (num_ant_paths + 1) < 5 ?
+					(num_ant_paths + 1) : 5;
+
+				*p++ = m2->ant_perm_index;
+
+				/* Serialize each path: PCT (3 bytes) +
+				 * quality (1 byte)
+				 */
+				for (k = 0; k < num_paths; k++) {
+					/* Convert 4-byte structure PCT to
+					 * 3-byte wire format
+					 */
+					uint32_t pct =
+						((uint32_t)(m2->tone_pct[k]
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m2->tone_pct[k]
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct & 0xFF);
+					*p++ = (uint8_t)((pct >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct >> 16) & 0xFF);
+
+					*p++ = m2->tone_quality_indicator[k];
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode2 "
+					"serialized payload_len=%u paths=%u",
+					idx, mode_byte, (unsigned int)plen,
+					num_paths);
+				break;
+			}
+			case CS_MODE_THREE: {
+				const struct cs_mode_three_data *m3 =
+					&step->step_mode_data.mode_three_data;
+				const struct cs_mode_one_data *m1 =
+					&m3->mode_one_data;
+				const struct cs_mode_two_data *m2 =
+					&m3->mode_two_data;
+				uint8_t buf[80];  /* Max Mode 3 size */
+				uint8_t *p = buf;
+				uint8_t k;
+				uint32_t pct1;
+				uint32_t pct2;
+				uint8_t num_paths = (num_ant_paths + 1) < 5 ?
+					(num_ant_paths + 1) : 5;
+				uint8_t cs_rtt_type = resptracker->rtt_type;
+				enum cs_role cs_role = resptracker->role;
+				uint16_t time_val;
+				bool include_pct;
+
+				/* Determine if PCT samples should be
+				 * included
+				 */
+				include_pct = (cs_rtt_type == 0x01 ||
+						cs_rtt_type == 0x02) &&
+					(cs_role == CS_ROLE_INITIATOR ||
+						cs_role == CS_ROLE_REFLECTOR);
+
+				/* Mode 1 portion - basic fields */
+				*p++ = m1->packet_quality;
+				*p++ = m1->packet_nadm;
+				*p++ = m1->packet_rssi_dbm;
+
+				/* Time value based on role */
+				if (cs_role == CS_ROLE_REFLECTOR)
+					time_val = m1->tod_toa_refl;
+				else
+					time_val = m1->toa_tod_init;
+
+				*p++ = (uint8_t)(time_val & 0xFF);
+				*p++ = (uint8_t)((time_val >> 8) & 0xFF);
+
+				*p++ = m1->packet_ant;
+
+				/* PCT samples if conditions met */
+				if (include_pct) {
+					pct1 = ((uint32_t)(m1->packet_pct1
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct1
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct1 & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct1 >> 16) & 0xFF);
+
+					pct2 = ((uint32_t)(m1->packet_pct2
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m1->packet_pct2
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct2 & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct2 >> 16) & 0xFF);
+				}
+
+				/* Mode 2 portion */
+				*p++ = m2->ant_perm_index;
+
+				for (k = 0; k < num_paths; k++) {
+					uint32_t pct =
+						((uint32_t)(m2->tone_pct[k]
+							.i_sample &
+							0x0FFF)) |
+						(((uint32_t)(m2->tone_pct[k]
+							.q_sample &
+							0x0FFF)) << 12);
+					*p++ = (uint8_t)(pct & 0xFF);
+					*p++ = (uint8_t)((pct >> 8) & 0xFF);
+					*p++ = (uint8_t)((pct >> 16) & 0xFF);
+
+					*p++ = m2->tone_quality_indicator[k];
+				}
+
+				plen = (uint8_t)(p - buf);
+				cs_pd_ras_append_subevent_bytes(proc, buf,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x Mode3 "
+					"serialized payload_len=%u paths=%u "
+					"role=%s rtt_type=0x%02x pct=%s",
+					idx, mode_byte, (unsigned int)plen,
+					num_paths,
+					cs_role == CS_ROLE_INITIATOR ?
+						"INIT" : "REFL",
+					cs_rtt_type,
+					include_pct ? "YES" : "NO");
+				break;
+			}
+			default:
+				/* Unknown mode: use raw structure bytes */
+				payload =
+					(const uint8_t *)&step->step_mode_data;
+				plen = step->step_data_length;
+				cs_pd_ras_append_subevent_bytes(proc, payload,
+								plen);
+				DBG(rap, "step[%u]: mode=0x%02x unknown mode, "
+					"payload_len=%u sent",
+					idx, mode_byte, (unsigned int)plen);
+				break;
+			}
+		}
+	}
+
+	/* Update status for this chunk */
+	cs_pd_set_local_status(proc,
+			(enum cs_procedure_done_status)proc_done_status);
+	cs_pd_set_remote_status(proc,
+			(enum cs_procedure_done_status)subevt_done_status);
+
+	/* Commit subevent chunk (RESULT or CONT) */
+	cs_pd_ras_commit_subevent(proc,
+		num_steps_reported,
+		proc_done_status,
+		subevt_done_status,
+		abort_reason & 0x0F,
+		(abort_reason >> 4) & 0x0F);
+
+	/* Ensure first segment body starts with the 4-byte RangingHeader */
+	ras_maybe_prepend_ranging_header(proc);
+
+	if (subevt_done_status != SUBEVENT_DONE_PARTIAL_RESULTS)
+		/* Send RAS raw segment data */
+		send_ras_segment_data(rap, proc);
+
+	/* Procedure complete? Clean up */
+	if (proc_done_status == CS_PROC_ALL_RESULTS_COMPLETE) {
+		DBG(rap, "Destroying CsProcedureData counter=%u and "
+			"clearing current_proc", proc->counter);
+		resptracker_reset_current_proc(resptracker);
+		/* Reset cached header values for next procedure */
+		resptracker->last_proc_counter = 0;
+		resptracker->last_start_acl_conn_evt_counter = 0;
+		resptracker->last_freq_comp = 0;
+		resptracker->last_ref_pwr_lvl = 0;
+	}
+}
+
+static void form_ras_data_with_cs_subevent_result(struct bt_rap *rap,
+		const struct rap_ev_cs_subevent_result *data,
+		uint16_t length)
+{
+	size_t base_len = offsetof(struct rap_ev_cs_subevent_result,
+					step_data);
+
+	if (!rap || !rap->resptracker || !data)
+		return;
+
+	/* Defensive check: base header must be present */
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result subevent: len=%d", length);
+
+	handle_local_subevent_result(rap,
+		true,			/* has header fields */
+		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);	/* start of steps */
+}
+static void form_ras_data_with_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 *resptracker;
+
+	if (!rap || !rap->resptracker || !cont)
+		return;
+
+	if (length < base_len)
+		return;
+
+	DBG(rap, "Received CS subevent result continue subevent: len=%d",
+		length);
+
+	resptracker = rap->resptracker;
+
+	/* Use cached header values captured from the last RESULT event */
+	handle_local_subevent_result(rap,
+		false,			/* CONT has no header fields */
+		cont->config_id,
+		cont->num_ant_paths,
+		resptracker->last_proc_counter,
+		resptracker->last_start_acl_conn_evt_counter,
+		resptracker->last_freq_comp,
+		resptracker->last_ref_pwr_lvl,
+		cont->proc_done_status,
+		cont->subevt_done_status,
+		cont->abort_reason,
+		cont->num_steps_reported,
+		cont->step_data);
+}
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_subevent_result_cont *cont = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent CONT: len=%d", length);
+
+	form_ras_data_with_cs_subevent_result_cont(rap, cont, length);
 }
 
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_subevent_result *data = param;
 	struct bt_rap *rap = user_data;
 
 	DBG(rap, "Received CS subevent: len=%d", length);
+
+	/* Populate CsProcedureData and send RAS payload */
+	form_ras_data_with_cs_subevent_result(rap, data, length);
 }
 
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
 						const void *param,
 						void *user_data)
 {
+	const struct rap_ev_cs_proc_enable_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
 
 	DBG(rap, "Received CS procedure enable complete subevent: len=%d",
 	    length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Populate responder tracker */
+	resptracker->config_id = data->config_id;
+	resptracker->selected_tx_power = data->sel_tx_pwr;
 }
 
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
@@ -544,9 +1878,27 @@ void bt_rap_hci_cs_config_complete_callback(uint16_t length,
 					const void *param,
 					void *user_data)
 {
+	const struct rap_ev_cs_config_cmplt *data = param;
 	struct bt_rap *rap = user_data;
+	struct cstracker *resptracker;
+
+	if (!rap)
+		return;
 
 	DBG(rap, "Received CS config complete subevent: len=%d", length);
+
+	if (!rap->resptracker) {
+		resptracker = new0(struct cstracker, 1);
+		cs_tracker_init(resptracker);
+		rap->resptracker = resptracker;
+	}
+
+	resptracker = rap->resptracker;
+
+	/* Basic fields */
+	resptracker->config_id = data->config_id;
+	resptracker->role = data->role;
+	resptracker->rtt_type = data->rtt_type;
 }
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
@@ -786,7 +2138,7 @@ bool bt_rap_attach(struct bt_rap *rap, struct bt_gatt_client *client)
 
 	bt_uuid16_create(&uuid, RAS_UUID16);
 
-	gatt_db_foreach_service(rap->lrapdb->db, &uuid,
+	gatt_db_foreach_service(rap->rrapdb->db, &uuid,
 				foreach_rap_service, rap);
 
 	return true;
diff --git a/src/shared/rap.h b/src/shared/rap.h
index 15ddea295..c9431aecd 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -97,11 +97,11 @@ struct cs_mode_zero_data {
 
 struct cs_mode_one_data {
 	uint8_t packet_quality;
-	uint8_t packet_rssi_dbm;
-	uint8_t packet_ant;
 	uint8_t packet_nadm;
+	uint8_t packet_rssi_dbm;
 	int16_t toa_tod_init;
 	int16_t tod_toa_refl;
+	uint8_t packet_ant;
 	struct pct_iq_sample packet_pct1;
 	struct pct_iq_sample packet_pct2;
 };
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v4 2/3] unit/test-rap: Add PTS tests for CS reflector
From: Prathibha Madugonde @ 2026-04-27 17:00 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427170007.1282289-1-prathm@qti.qualcomm.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 11085 bytes --]

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

Added below RAS - Real time Ranging PTS testcases

RAS/SR/RCO/BV-01-C [Characteristic Read - RAS Features]
RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
---
 unit/test-rap.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 260 insertions(+), 4 deletions(-)

diff --git a/unit/test-rap.c b/unit/test-rap.c
index 884cb1c96..6f65967a4 100644
--- a/unit/test-rap.c
+++ b/unit/test-rap.c
@@ -37,6 +37,7 @@ struct test_data_ras {
 	size_t iovcnt;
 	struct iovec *iov;
 	unsigned int ras_id;
+	struct bt_rap *rap;  /* Store rap instance for CS injection */
 };
 
 struct test_data_rap {
@@ -68,8 +69,8 @@ struct notify {
 	do {							\
 		const struct iovec iov[] = { args };		\
 		static struct test_data_ras data;			\
-		data.iovcnt = ARRAY_SIZE(iov_data(args));	\
-		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov_data(args))); \
+		data.iovcnt = ARRAY_SIZE(iov);	\
+		data.iov = util_iov_dup(iov, ARRAY_SIZE(iov)); \
 		tester_add(name, &data, NULL, function,	\
 				test_teardown_ras);			\
 	} while (0)
@@ -155,6 +156,94 @@ static void gatt_notify_cb(struct gatt_db_attribute *attrib,
 		printf("%s: Failed to send notification\n", __func__);
 }
 
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct test_data_ras *data = user_data;
+	struct ccc_state *ccc;
+	uint16_t handle;
+	uint16_t ccc_value;
+	uint8_t ecode = 0;
+
+	handle = gatt_db_attribute_get_handle(attrib);
+
+	if (offset) {
+		ecode = BT_ATT_ERROR_INVALID_OFFSET;
+		goto done;
+	}
+
+	if (len != 2) {
+		ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+		goto done;
+	}
+
+	ccc_value = get_le16(value);
+
+	ccc = get_ccc_state(data, handle);
+	if (!ccc) {
+		ecode = BT_ATT_ERROR_UNLIKELY;
+		goto done;
+	}
+
+	ccc->value = ccc_value;
+
+	/* Send write response first */
+	gatt_db_attribute_write_result(attrib, id, 0);
+
+	/* If notifications/indications enabled on Real-time Ranging Data CCCD,
+	 * inject fake HCI CS subevent data to trigger notifications
+	 */
+	if (handle == 0x0006 && ccc_value != 0x0000 && data->rap) {
+		size_t event_size = sizeof(struct rap_ev_cs_subevent_result) +
+				    sizeof(struct cs_step_data);
+		struct rap_ev_cs_subevent_result *fake_event;
+		struct cs_mode_zero_data *mode_zero;
+
+		if (tester_use_debug())
+			tester_debug("Injecting fake CS data...");
+
+		fake_event = g_malloc0(event_size);
+		if (!fake_event)
+			return;  /* Already sent write response */
+
+		/* Fill in the header fields */
+		fake_event->conn_hdl = 0x0001;
+		fake_event->config_id = 0x01;
+		fake_event->start_acl_conn_evt_counter = 0x0000;
+		fake_event->proc_counter = 0x0001;
+		fake_event->freq_comp = 0x0000;
+		fake_event->ref_pwr_lvl = 0x00;
+		fake_event->proc_done_status = 0x00;
+		fake_event->subevt_done_status = 0x00;
+		fake_event->abort_reason = 0x00;
+		fake_event->num_ant_paths = 0x01;
+		fake_event->num_steps_reported = 0x01;
+
+		fake_event->step_data[0].step_mode = CS_MODE_ZERO;
+		fake_event->step_data[0].step_chnl = 0x00;
+		/* Mode 0: 1+1+1+4 bytes */
+		fake_event->step_data[0].step_data_length = 4;
+mode_zero = &fake_event->step_data[0].step_mode_data.mode_zero_data;
+		mode_zero->packet_quality = 0x01;
+		mode_zero->packet_rssi_dbm = 0x02;
+		mode_zero->packet_ant = 0x03;
+		mode_zero->init_measured_freq_offset = 0x04;
+
+		/* Inject the fake event to trigger notification */
+		bt_rap_hci_cs_subevent_result_callback(event_size, fake_event,
+						       data->rap);
+
+		g_free(fake_event);
+	}
+	return;
+
+done:
+	gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
 static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
 					unsigned int id, uint16_t offset,
 					uint8_t opcode, struct bt_att *att,
@@ -184,10 +273,21 @@ done:
 
 static void ras_attached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data) {
+		data->rap = rap;
+		bt_rap_ref(rap);  /* Keep a reference */
+	}
 }
 
 static void ras_detached(struct bt_rap *rap, void *user_data)
 {
+	struct test_data_ras *data = user_data;
+
+	if (data && data->rap == rap)
+		data->rap = NULL;
+
 	bt_rap_unref(rap);
 }
 
@@ -205,17 +305,18 @@ static void test_server(const void *user_data)
 	att = bt_att_new(io_get_fd(io), false);
 	g_assert(att);
 
+	bt_att_set_security(att, BT_ATT_SECURITY_MEDIUM);
 	bt_att_set_debug(att, BT_ATT_DEBUG, print_debug, "bt_att:", NULL);
 
 	data->db = gatt_db_new();
 	g_assert(data->db);
 
-	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, NULL,
+	gatt_db_ccc_register(data->db, gatt_ccc_read_cb, gatt_ccc_write_cb,
 					gatt_notify_cb, data);
 
 	bt_rap_add_db(data->db);
 
-	data->ras_id = bt_rap_register(ras_attached, ras_detached, NULL);
+	data->ras_id = bt_rap_register(ras_attached, ras_detached, data);
 
 	data->server = bt_gatt_server_new(data->db, att, 64, 0);
 	g_assert(data->server);
@@ -426,6 +527,150 @@ static void test_server(const void *user_data)
 	DISC_RAS_CHAR_AFTER_TYPE, \
 	RAS_FIND_INFO
 
+/*
+ * RAS/SR/RCO/BV-01-C – Characteristic Read: RAS Features
+ *
+ *  ATT: Read Request (0x0a) len 2
+ *       Handle: 0x0003 (RAS Features value handle)
+ *
+ *  ATT: Read Response (0x0b) len 5
+ *       Value: 0x01 0x00 0x00 0x00
+ *       Feature bits:
+ *         Bit 0: Real-time ranging (1 = supported)
+ *         Bit 1: Retrieve stored results (0 = not supported)
+ *         Bit 2: Abort operation (0 = not supported)
+ *
+ *  Note: The RAS Features characteristic is registered with
+ *  BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT. Since the test sets
+ *  BT_ATT_SECURITY_MEDIUM, the encryption requirement is satisfied
+ *  and the server returns the feature value showing real-time ranging
+ *  support.
+ */
+
+#define ATT_READ_RAS_FEATURES \
+	IOV_DATA(0x0a, 0x03, 0x00), \
+	IOV_DATA(0x0b, 0x01, 0x00, 0x00, 0x00)
+
+#define RAS_SR_RCO_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	ATT_READ_RAS_FEATURES
+
+/*
+ * RAS Real-time Ranging Data CCCD Configuration
+ * Round 1: Enable/Disable notifications (CCCD = 0x0001)
+ * Round 2: Enable/Disable indications (CCCD = 0x0002)
+ */
+#define RAS_REALTIME_CCCD_CONFIG \
+	/* Round 1: Enable notifications on Real-time Ranging Data CCCD */ \
+	/* (handle 0x0006) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Round 2: Enable indications on Real-time Ranging Data CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x02, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable indications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Enable both notifications and indications (CCCD = 0x0003)
+ * Expect notification (0x1b) to be sent, not indication (0x1d)
+ * Then disable CCCD
+ *
+ * Note: This test is currently disabled because the GATT server rejects
+ * CCCD value 0x0003 (both notifications and indications enabled) before
+ * reaching the custom callback. The test infrastructure needs to be updated
+ * to support this scenario.
+ */
+#define RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE \
+	/* Enable notifications only (CCCD = 0x0001) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * Disconnection/Reconnection simulation for Real-time Ranging Data
+ * Enable notifications, disable (disconnect), re-enable (reconnect), disable
+ */
+#define RAS_REALTIME_CCCD_DISCONNECT_RECONNECT \
+	/* Step 1: Enable notifications */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 3: Disable CCCD (simulates disconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13), \
+	/* Step 4: Re-enable notifications (simulates reconnection) */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x01, 0x00), \
+	IOV_DATA(0x13), \
+	/* Disable CCCD to clean up */ \
+	IOV_DATA(0x12, 0x06, 0x00, 0x00, 0x00), \
+	IOV_DATA(0x13)
+
+/*
+ * RAS/SR/RRD/BV-01-C [Real-time Ranging Data]
+ * Verify that the IUT can configure CCCD for notifications/indications
+ * of the Real-time Ranging Data characteristic.
+ */
+#define RAS_SR_RRD_BV_01_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_CONFIG
+
+/*
+ * RAS/SR/RRD/BV-03-C [Real-time Ranging Data notifications and indications]
+ * Verify that the IUT only sends Real-time Ranging Data notifications when
+ * configured for both notifications and indications (CCCD = 0x0003).
+ *
+ * Test Procedure:
+ * 1. Write 0x0003 to Real-time Ranging Data CCCD (enable both
+ *    notifications and indications)
+ * 2. Trigger CS Subevent Data (via fake HCI event injection)
+ * 3. Verify IUT sends only notifications (0x1b), not indications (0x1d)
+ */
+#define RAS_SR_RRD_BV_03_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_BOTH_ENABLE_DISABLE
+
+/*
+ * RAS/SR/RRD/BV-05-C [Real-Time Ranging Data disconnection]
+ * Verify that the IUT does not resume sending Real-time Ranging Data
+ * notifications or indications after a disconnection occurs.
+ *
+ * Test Procedure:
+ * 1. Enable Real-time Ranging Data notifications
+ * 2. Trigger CS Subevent Data (IUT sends notifications)
+ * 3. Disable CCCD (simulates disconnection - CCCD resets to 0x0000)
+ * 4. Re-enable notifications (simulates reconnection and reconfiguration)
+ * 5. Verify IUT does not send old ranging data
+ *
+ * Note: In a unit test, we simulate disconnection by disabling and re-enabling
+ * the CCCD. The RAP implementation should clear any pending data when CCCD is
+ * disabled, ensuring no old data is sent after re-enabling.
+ */
+#define RAS_SR_RRD_BV_05_C \
+	ATT_EXCHANGE_MTU, \
+	DISCOVER_PRIM_SERV_NOTIF, \
+	RAS_FIND_BY_TYPE_VALUE, \
+	DISC_RAS_CHAR_AFTER_TYPE, \
+	RAS_FIND_INFO, \
+	RAS_REALTIME_CCCD_DISCONNECT_RECONNECT
+
 int main(int argc, char *argv[])
 {
 	tester_init(&argc, &argv);
@@ -441,6 +686,17 @@ int main(int argc, char *argv[])
 					RAS_SR_SGGIT_CHA_BV_03_C);
 	define_test_ras("RAS/SR/SGGIT/CHA/BV-04-C", test_server,
 					RAS_SR_SGGIT_CHA_BV_04_C);
+	/* RAS Read Characteristic Operations */
+	define_test_ras("RAS/SR/RCO/BV-01-C", test_server,
+					RAS_SR_RCO_BV_01_C);
+	/* RAS Real-time Ranging Data */
+	define_test_ras("RAS/SR/RRD/BV-01-C", test_server,
+					RAS_SR_RRD_BV_01_C);
+	/* RAS Real-time Ranging Data with CS injection */
+	define_test_ras("RAS/SR/RRD/BV-03-C", test_server,
+					RAS_SR_RRD_BV_03_C);
+	define_test_ras("RAS/SR/RRD/BV-05-C", test_server,
+					RAS_SR_RRD_BV_05_C);
 
 	return tester_run();
 }
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ v4 3/3] profiles/ranging: Read cs_mode_one_data members as per spec
From: Prathibha Madugonde @ 2026-04-27 17:00 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg
In-Reply-To: <20260427170007.1282289-1-prathm@qti.qualcomm.com>

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

Rearrage reading of cs_mode_one_data struct members as per spec.
---
 profiles/ranging/rap_hci.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
index a3d2df608..08ddc077c 100644
--- a/profiles/ranging/rap_hci.c
+++ b/profiles/ranging/rap_hci.c
@@ -632,19 +632,22 @@ static void parse_mode_one_data(struct iovec *iov,
 	}
 
 	DBG("CS Step mode 1");
-	util_iov_pull_u8(iov, &mode_data->packet_quality);
-	util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm);
-	util_iov_pull_u8(iov, &mode_data->packet_ant);
-	util_iov_pull_u8(iov, &mode_data->packet_nadm);
-
-	if (iov->iov_len >= 2) {
-		util_iov_pull_le16(iov, &time_val);
-		if (cs_role == CS_REFLECTOR)
-			mode_data->tod_toa_refl = time_val;
-		else
-			mode_data->toa_tod_init = time_val;
+	/* Parse fixed fields in specification order */
+	if (!util_iov_pull_u8(iov, &mode_data->packet_quality) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_nadm) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_rssi_dbm) ||
+		!util_iov_pull_le16(iov, &time_val) ||
+		!util_iov_pull_u8(iov, &mode_data->packet_ant)) {
+		DBG("Mode 1: failed to parse basic fields");
+		memset(mode_data, 0, sizeof(*mode_data));
+		return;
 	}
 
+	if (cs_role == CS_REFLECTOR)
+		mode_data->tod_toa_refl = time_val;
+	else
+		mode_data->toa_tod_init = time_val;
+
 	if ((cs_rtt_type == 0x01 || cs_rtt_type == 0x02) &&
 		iov->iov_len >= 6) {
 		int16_t i_val, q_val;
-- 
2.34.1


^ permalink raw reply related

* [PATCH BlueZ 0/3] Add ability to desynchronized transports for PTS tests
From: Frédéric Danis @ 2026-04-27 17:20 UTC (permalink / raw)
  To: linux-bluetooth

If bluetoothd is started in testing mode a new Desynchronized property
is added to org.bluez.MediaTransport1, allowing to prevent automatic
acquire of linked transport objects.

When desynchronized, each transports needs to be acquired separately.

This allows to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C which
requires to connect the ISO streams with one ASE in "Enable" state
while the other is in "Config QoS" state.

Frédéric Danis (3):
  audio: Add ability to desynchronized linked transport
  doc: Add documentation for CIS transport Desynchronized property
  client/player: Add support to desynchronize linked transports

 client/player.c                  |  76 ++++++++++++++++++++-
 doc/org.bluez.MediaTransport.rst |  14 ++++
 profiles/audio/transport.c       | 113 +++++++++++++++++++++++++++----
 3 files changed, 189 insertions(+), 14 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH BlueZ 1/3] audio: Add ability to desynchronized linked transport
From: Frédéric Danis @ 2026-04-27 17:20 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260427172056.148115-1-frederic.danis@collabora.com>

If bluetoothd is started in testing mode a new Desynchronized property
is added, allowing to prevent automatic acquire of linked transport
objects.

When desynchronized, each transports needs to be acquired separately.

This allows to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.
---
 profiles/audio/transport.c | 113 ++++++++++++++++++++++++++++++++-----
 1 file changed, 100 insertions(+), 13 deletions(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 5c2a2777e..f8f68737d 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -37,6 +37,7 @@
 #include "src/shared/bap.h"
 #include "src/shared/bass.h"
 #include "src/shared/io.h"
+#include "src/btd.h"
 
 #ifdef HAVE_A2DP
 #include "avdtp.h"
@@ -109,11 +110,13 @@ struct bap_transport {
 	guint			resume_id;
 	struct iovec		*meta;
 	guint			chan_id;
+	bool			desynchronized;
 };
 
 struct media_transport_ops {
 	const char *uuid;
 	const GDBusPropertyTable *properties;
+	const GDBusPropertyTable *test_properties;
 	void (*set_owner)(struct media_transport *transport,
 				struct media_owner *owner);
 	void (*remove_owner)(struct media_transport *transport,
@@ -351,7 +354,7 @@ static void transport_bap_remove_owner(struct media_transport *transport,
 {
 	struct bap_transport *bap = transport->data;
 
-	if (bap && bap->linked)
+	if (bap && bap->linked && !bap->desynchronized)
 		queue_foreach(bt_bap_stream_io_get_links(bap->stream),
 				linked_transport_remove_owner, owner);
 }
@@ -729,7 +732,7 @@ static void transport_bap_set_owner(struct media_transport *transport,
 {
 	struct bap_transport *bap = transport->data;
 
-	if (bap && bap->linked)
+	if (bap && bap->linked && !bap->desynchronized)
 		queue_foreach(bt_bap_stream_io_get_links(bap->stream),
 				linked_transport_set_owner, owner);
 }
@@ -1463,6 +1466,60 @@ static void set_links(const GDBusPropertyTable *property,
 	g_dbus_pending_property_success(id);
 }
 
+static gboolean get_desynchronized(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct media_transport *transport = data;
+	struct bap_transport *bap = transport->data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+							&bap->desynchronized);
+
+	return TRUE;
+}
+
+static void transport_desynchronize(void *data, void *user_data)
+{
+	struct bt_bap_stream *link = data;
+	bool desynchronized = PTR_TO_UINT(user_data);
+	struct media_transport *transport;
+	struct bap_transport *bap;
+
+	transport = find_transport_by_bap_stream(link);
+	bap = transport->data;
+	bap->desynchronized = desynchronized;
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"Desynchronized");
+}
+
+static void set_desynchronized(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct media_transport *transport = data;
+	struct bap_transport *bap = transport->data;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+				ERROR_INTERFACE ".InvalidArguments",
+				"Expected BOOLEAN");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &bap->desynchronized);
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"Desynchronized");
+
+	/* Set desynchronized for all the links */
+	queue_foreach(bt_bap_stream_io_get_links(bap->stream),
+					transport_desynchronize,
+					UINT_TO_PTR(bap->desynchronized));
+
+	g_dbus_pending_property_success(id);
+}
+
 static gboolean qos_ucast_exists(const GDBusPropertyTable *property, void *data)
 {
 	struct media_transport *transport = data;
@@ -1486,6 +1543,23 @@ static const GDBusPropertyTable transport_bap_uc_properties[] = {
 	{ }
 };
 
+static const GDBusPropertyTable transport_bap_uc_test_properties[] = {
+	{ "Device", "o", get_device },
+	{ "UUID", "s", get_uuid },
+	{ "Codec", "y", get_codec },
+	{ "Configuration", "ay", get_configuration },
+	{ "State", "s", get_state },
+	{ "QoS", "a{sv}", get_ucast_qos, NULL, qos_ucast_exists },
+	{ "Endpoint", "o", get_endpoint, NULL, endpoint_exists },
+	{ "Location", "u", get_location },
+	{ "Metadata", "ay", get_metadata, set_metadata },
+	{ "Links", "ao", get_links, NULL, links_exists },
+	{ "Volume", "q", get_volume, set_volume, volume_exists },
+	{ "Desynchronized", "b", get_desynchronized, set_desynchronized,
+								links_exists},
+	{ }
+};
+
 static gboolean get_bcast_qos(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
@@ -1920,6 +1994,9 @@ static void transport_bap_update_links_uc(
 	g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
 						MEDIA_TRANSPORT_INTERFACE,
 						"Links");
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
+						MEDIA_TRANSPORT_INTERFACE,
+						"Desynchronized");
 
 	DBG("stream %p linked %s", bap->stream, bap->linked ? "true" : "false");
 }
@@ -2037,8 +2114,9 @@ static guint transport_bap_resume(struct media_transport *transport,
 		return bap->resume_id;
 	}
 
-	id = bt_bap_stream_enable(bap->stream, bap->linked, NULL,
-					bap_enable_complete, owner);
+	id = bt_bap_stream_enable(bap->stream,
+				bap->desynchronized ? false : bap->linked,
+				NULL, bap_enable_complete, owner);
 	if (!id)
 		return 0;
 
@@ -2161,7 +2239,7 @@ static void transport_bap_set_state(struct media_transport *transport,
 {
 	struct bap_transport *bap = transport->data;
 
-	if (!bap->linked)
+	if (!bap->linked || bap->desynchronized)
 		return;
 
 	/* Update links */
@@ -2540,10 +2618,11 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 #define TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner, _init, \
 		      _resume, _suspend, _cancel, _set_state, _get_stream, \
 		      _get_volume, _set_volume, _set_delay, _update_links, \
-		      _destroy) \
+		      _destroy, _test_props) \
 { \
 	.uuid = _uuid, \
 	.properties = _props, \
+	.test_properties = _test_props, \
 	.set_owner = _set_owner, \
 	.remove_owner = _remove_owner, \
 	.init = _init, \
@@ -2565,26 +2644,28 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 			transport_a2dp_resume, transport_a2dp_suspend, \
 			transport_a2dp_cancel, NULL, \
 			transport_a2dp_get_stream, transport_a2dp_get_volume, \
-			_set_volume, _set_delay, NULL, _destroy)
+			_set_volume, _set_delay, NULL, _destroy, NULL)
 
 #define BAP_OPS(_uuid, _props, _set_owner, _remove_owner, _update_links, \
-		_set_state) \
+		_set_state, _test_props) \
 	TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner,\
 			transport_bap_init, \
 			transport_bap_resume, transport_bap_suspend, \
 			transport_bap_cancel, _set_state, \
 			transport_bap_get_stream, transport_bap_get_volume, \
 			transport_bap_set_volume, NULL, \
-			_update_links, transport_bap_destroy)
+			_update_links, transport_bap_destroy, _test_props)
 
 #define BAP_UC_OPS(_uuid) \
 	BAP_OPS(_uuid, transport_bap_uc_properties, \
 			transport_bap_set_owner, transport_bap_remove_owner, \
-			transport_bap_update_links_uc, transport_bap_set_state)
+			transport_bap_update_links_uc, \
+			transport_bap_set_state, \
+			transport_bap_uc_test_properties)
 
 #define BAP_BC_OPS(_uuid) \
 	BAP_OPS(_uuid, transport_bap_bc_properties, NULL, NULL, \
-			transport_bap_update_links_bc, NULL)
+			transport_bap_update_links_bc, NULL, NULL)
 
 #define ASHA_OPS(_uuid) \
 	TRANSPORT_OPS(_uuid, transport_asha_properties, NULL, NULL, \
@@ -2592,7 +2673,7 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 			transport_asha_resume, transport_asha_suspend, \
 			transport_asha_cancel, NULL, NULL, \
 			transport_asha_get_volume, transport_asha_set_volume, \
-			NULL, NULL, NULL)
+			NULL, NULL, NULL, NULL)
 
 static const struct media_transport_ops transport_ops[] = {
 #ifdef HAVE_A2DP
@@ -2647,6 +2728,7 @@ struct media_transport *media_transport_create(struct btd_device *device,
 	struct media_transport *transport;
 	const struct media_transport_ops *ops;
 	int fd;
+	const GDBusPropertyTable *properties;
 
 	transport = g_new0(struct media_transport, 1);
 	if (device)
@@ -2701,9 +2783,14 @@ struct media_transport *media_transport_create(struct btd_device *device,
 			goto fail;
 	}
 
+	if (btd_opts.testing && ops->test_properties)
+		properties = ops->test_properties;
+	else
+		properties = ops->properties;
+
 	if (g_dbus_register_interface(btd_get_dbus_connection(),
 				transport->path, MEDIA_TRANSPORT_INTERFACE,
-				transport_methods, NULL, ops->properties,
+				transport_methods, NULL, properties,
 				transport, media_transport_free) == FALSE) {
 		error("Could not register transport %s", transport->path);
 		goto fail;
-- 
2.43.0


^ permalink raw reply related

* [PATCH BlueZ 2/3] doc: Add documentation for CIS transport Desynchronized property
From: Frédéric Danis @ 2026-04-27 17:20 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260427172056.148115-1-frederic.danis@collabora.com>

This is only supported when bluetoothd is started in testing mode.

Assisted-by: GPT:GPT-5.3-Codex
---
 doc/org.bluez.MediaTransport.rst | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/doc/org.bluez.MediaTransport.rst b/doc/org.bluez.MediaTransport.rst
index 81cf9e4da..e50ec3f20 100644
--- a/doc/org.bluez.MediaTransport.rst
+++ b/doc/org.bluez.MediaTransport.rst
@@ -357,3 +357,17 @@ Possible values for both Unicast and Broadcast:
 :byte Retransmissions:
 
 	Indicates configured retransmissions.
+
+boolean Desynchronized [readwrite, optional, CIS only, testing, experimental]
+`````````````````````````````````````````````````````````````````````````````
+
+Available only when D-Bus testing interfaces are enabled and the transport has
+the **Links** property.
+
+Controls whether linked transports are acquired together.
+
+When set to False (default), acquiring one linked transport automatically
+acquires all links.
+
+When set to True, links are not acquired automatically and each link must be
+acquired separately.
-- 
2.43.0


^ permalink raw reply related

* [PATCH BlueZ 3/3] client/player: Add support to desynchronize linked transports
From: Frédéric Danis @ 2026-04-27 17:20 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <20260427172056.148115-1-frederic.danis@collabora.com>

After linked transports has been desynchronized, they should be acquired
separately and read handler should only be started for source local
endpoint.

This is used to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.
---
 client/player.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/client/player.c b/client/player.c
index 1444e939d..c5e4beef0 100644
--- a/client/player.c
+++ b/client/player.c
@@ -5003,6 +5003,9 @@ static bool transport_recv(struct io *io, void *user_data)
 static void transport_new(GDBusProxy *proxy, int sk, uint16_t mtu[2])
 {
 	struct transport *transport;
+	DBusMessageIter iter;
+	const char *uuid;
+	bool reader = true;
 
 	transport = new0(struct transport, 1);
 	transport->proxy = proxy;
@@ -5014,7 +5017,27 @@ static void transport_new(GDBusProxy *proxy, int sk, uint16_t mtu[2])
 
 	io_set_disconnect_handler(transport->io, transport_disconnected,
 							transport, NULL);
-	io_set_read_handler(transport->io, transport_recv, transport, NULL);
+
+	if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	/* For BAP testing, streams may have been manually desynchronized.
+	 * In this case source and sink streams are acquired separately and
+	 * read handler should not be started for source local endpoint.
+	 */
+	if (g_dbus_proxy_get_property(proxy, "Desynchronized", &iter)) {
+		dbus_bool_t desync;
+
+		dbus_message_iter_get_basic(&iter, &desync);
+		if (desync && !strcmp(uuid, PAC_SOURCE_UUID))
+			reader = false;
+	}
+
+	if (reader)
+		io_set_read_handler(transport->io, transport_recv, transport,
+									NULL);
 
 	if (!ios)
 		ios = queue_new();
@@ -5325,6 +5348,7 @@ static void print_transport_properties(GDBusProxy *proxy)
 	print_property(proxy, "QoS");
 	print_property(proxy, "Location");
 	print_property(proxy, "Links");
+	print_property(proxy, "Desynchronized");
 }
 
 static void print_transports(void *data, void *user_data)
@@ -6091,6 +6115,53 @@ static void cmd_metadata_transport(int argc, char *argv[])
 	}
 }
 
+static void desync_cb(const DBusError *error, void *user_data)
+{
+	if (dbus_error_is_set(error)) {
+		bt_shell_printf("Failed to desynchronize: %s\n", error->name);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Desynchronize succeeded\n");
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_desync_transport(int argc, char *argv[])
+{
+	dbus_bool_t desync;
+	GDBusProxy *proxy;
+
+	if (argc < 3) {
+		bt_shell_printf("Missing argument to %s\n", argv[0]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+					BLUEZ_MEDIA_TRANSPORT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Transport %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes")) {
+		desync = TRUE;
+	} else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no")) {
+		desync = FALSE;
+	} else {
+		bt_shell_printf("Invalid argument %s\n", argv[2]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (!g_dbus_proxy_set_property_basic(proxy, "Desynchronized",
+							DBUS_TYPE_BOOLEAN,
+							&desync, desync_cb,
+							NULL, NULL)) {
+		bt_shell_printf("Failed to desynchronize transport\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
 static const struct bt_shell_menu transport_menu = {
 	.name = "transport",
 	.desc = "Media Transport Submenu",
@@ -6126,6 +6197,9 @@ static const struct bt_shell_menu transport_menu = {
 	{ "metadata",    "<transport> [value...]", cmd_metadata_transport,
 						"Get/Set Transport Metadata",
 						transport_generator },
+	{ "desync",      "<transport> <on/off>", cmd_desync_transport,
+						"Desynchronize Transport",
+						transport_generator },
 	{} },
 };
 
-- 
2.43.0


^ permalink raw reply related

* [Bug 221426] Intel AX211 Bluetooth hci0 hardware error 0x0c with HID/HOG failures on 6.19.14-zen
From: bugzilla-daemon @ 2026-04-27 17:26 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <bug-221426-62941@https.bugzilla.kernel.org/>

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

Paul Menzel (pmenzel+bugzilla.kernel.org@molgen.mpg.de) changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |pmenzel+bugzilla.kernel.org
                   |                            |@molgen.mpg.de

--- Comment #2 from Paul Menzel (pmenzel+bugzilla.kernel.org@molgen.mpg.de) ---
Thank you for your report. Do you know if it’s a regression, that means, did it
work with earlier Linux versions?

What firmware is used?

The BlueZ issue template has some more commands helping to collect things
aiding in debugging.


[1]: https://github.com/bluez/bluez/tree/master/.github/ISSUE_TEMPLATE

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* [bluez/bluez] 5858e2: src/shared: Add RAS packet formatting and notifica...
From: prathibhamadugonde @ 2026-04-27 17:58 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1086284
  Home:   https://github.com/bluez/bluez
  Commit: 5858e22147395b6b6660c241fadd9c221292e086
      https://github.com/bluez/bluez/commit/5858e22147395b6b6660c241fadd9c221292e086
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M src/shared/rap.c
    M src/shared/rap.h

  Log Message:
  -----------
  src/shared: Add RAS packet formatting and notification support for CS reflector

Implement complete RAS data pipeline:
 - Handle HCI CS subevent result/continuation events
 - Serialize ranging/subevent headers per spec
 - GATT notifications for real-time ranging data


  Commit: 3189561250284aade1a61715ddf85052531d3842
      https://github.com/bluez/bluez/commit/3189561250284aade1a61715ddf85052531d3842
  Author: Prathibha Madugonde <prathibha.madugonde@oss.qualcomm.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M profiles/ranging/rap_hci.c

  Log Message:
  -----------
  profiles/ranging: Read cs_mode_one_data members as per spec

Rearrage reading of cs_mode_one_data struct members as per spec.


Compare: https://github.com/bluez/bluez/compare/5858e2214739%5E...318956125028

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

^ permalink raw reply

* [bluez/bluez] c18634: audio: Add ability to desynchronized linked transport
From: fdanis-oss @ 2026-04-27 17:58 UTC (permalink / raw)
  To: linux-bluetooth

  Branch: refs/heads/1086318
  Home:   https://github.com/bluez/bluez
  Commit: c1863432872e715acaee7ddeb32e93d6a9149983
      https://github.com/bluez/bluez/commit/c1863432872e715acaee7ddeb32e93d6a9149983
  Author: Frédéric Danis <frederic.danis@collabora.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M profiles/audio/transport.c

  Log Message:
  -----------
  audio: Add ability to desynchronized linked transport

If bluetoothd is started in testing mode a new Desynchronized property
is added, allowing to prevent automatic acquire of linked transport
objects.

When desynchronized, each transports needs to be acquired separately.

This allows to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.


  Commit: db2fc7fa960a711f3e6c4691c4f73858a44ef23a
      https://github.com/bluez/bluez/commit/db2fc7fa960a711f3e6c4691c4f73858a44ef23a
  Author: Frédéric Danis <frederic.danis@collabora.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M doc/org.bluez.MediaTransport.rst

  Log Message:
  -----------
  doc: Add documentation for CIS transport Desynchronized property

This is only supported when bluetoothd is started in testing mode.

Assisted-by: GPT:GPT-5.3-Codex


  Commit: 9100a7fccc84fc1f9d0c7b49612900297d94e187
      https://github.com/bluez/bluez/commit/9100a7fccc84fc1f9d0c7b49612900297d94e187
  Author: Frédéric Danis <frederic.danis@collabora.com>
  Date:   2026-04-27 (Mon, 27 Apr 2026)

  Changed paths:
    M client/player.c

  Log Message:
  -----------
  client/player: Add support to desynchronize linked transports

After linked transports has been desynchronized, they should be acquired
separately and read handler should only be started for source local
endpoint.

This is used to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.


Compare: https://github.com/bluez/bluez/compare/c1863432872e%5E...9100a7fccc84

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

^ permalink raw reply

* Re: [PATCH BlueZ 1/3] audio: Add ability to desynchronized linked transport
From: Luiz Augusto von Dentz @ 2026-04-27 18:02 UTC (permalink / raw)
  To: Frédéric Danis; +Cc: linux-bluetooth
In-Reply-To: <20260427172056.148115-2-frederic.danis@collabora.com>

Hi Frédéric,

On Mon, Apr 27, 2026 at 1:26 PM Frédéric Danis
<frederic.danis@collabora.com> wrote:
>
> If bluetoothd is started in testing mode a new Desynchronized property
> is added, allowing to prevent automatic acquire of linked transport
> objects.
>
> When desynchronized, each transports needs to be acquired separately.
>
> This allows to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.
> ---
>  profiles/audio/transport.c | 113 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 100 insertions(+), 13 deletions(-)
>
> diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
> index 5c2a2777e..f8f68737d 100644
> --- a/profiles/audio/transport.c
> +++ b/profiles/audio/transport.c
> @@ -37,6 +37,7 @@
>  #include "src/shared/bap.h"
>  #include "src/shared/bass.h"
>  #include "src/shared/io.h"
> +#include "src/btd.h"
>
>  #ifdef HAVE_A2DP
>  #include "avdtp.h"
> @@ -109,11 +110,13 @@ struct bap_transport {
>         guint                   resume_id;
>         struct iovec            *meta;
>         guint                   chan_id;
> +       bool                    desynchronized;

Let's call it 'desync' for short. Also, add a comment stating that
this is only writable when testing is enabled; otherwise, it defaults
to false, meaning the Enable/Acquire operations synchronize
input/output directions that share the same ISO socket CID/CIG.

>  };
>
>  struct media_transport_ops {
>         const char *uuid;
>         const GDBusPropertyTable *properties;
> +       const GDBusPropertyTable *test_properties;
>         void (*set_owner)(struct media_transport *transport,
>                                 struct media_owner *owner);
>         void (*remove_owner)(struct media_transport *transport,
> @@ -351,7 +354,7 @@ static void transport_bap_remove_owner(struct media_transport *transport,
>  {
>         struct bap_transport *bap = transport->data;
>
> -       if (bap && bap->linked)
> +       if (bap && bap->linked && !bap->desynchronized)
>                 queue_foreach(bt_bap_stream_io_get_links(bap->stream),
>                                 linked_transport_remove_owner, owner);
>  }
> @@ -729,7 +732,7 @@ static void transport_bap_set_owner(struct media_transport *transport,
>  {
>         struct bap_transport *bap = transport->data;
>
> -       if (bap && bap->linked)
> +       if (bap && bap->linked && !bap->desynchronized)
>                 queue_foreach(bt_bap_stream_io_get_links(bap->stream),
>                                 linked_transport_set_owner, owner);
>  }
> @@ -1463,6 +1466,60 @@ static void set_links(const GDBusPropertyTable *property,
>         g_dbus_pending_property_success(id);
>  }
>
> +static gboolean get_desynchronized(const GDBusPropertyTable *property,
> +                                       DBusMessageIter *iter, void *data)
> +{
> +       struct media_transport *transport = data;
> +       struct bap_transport *bap = transport->data;
> +
> +       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
> +                                                       &bap->desynchronized);
> +
> +       return TRUE;
> +}
> +
> +static void transport_desynchronize(void *data, void *user_data)
> +{
> +       struct bt_bap_stream *link = data;
> +       bool desynchronized = PTR_TO_UINT(user_data);
> +       struct media_transport *transport;
> +       struct bap_transport *bap;
> +
> +       transport = find_transport_by_bap_stream(link);
> +       bap = transport->data;
> +       bap->desynchronized = desynchronized;
> +       g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
> +                                               MEDIA_TRANSPORT_INTERFACE,
> +                                               "Desynchronized");
> +}
> +
> +static void set_desynchronized(const GDBusPropertyTable *property,
> +                       DBusMessageIter *iter, GDBusPendingPropertySet id,
> +                       void *data)
> +{
> +       struct media_transport *transport = data;
> +       struct bap_transport *bap = transport->data;
> +
> +       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
> +               g_dbus_pending_property_error(id,
> +                               ERROR_INTERFACE ".InvalidArguments",
> +                               "Expected BOOLEAN");
> +               return;
> +       }
> +
> +       dbus_message_iter_get_basic(iter, &bap->desynchronized);
> +       g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
> +                                               MEDIA_TRANSPORT_INTERFACE,
> +                                               "Desynchronized");
> +
> +       /* Set desynchronized for all the links */
> +       queue_foreach(bt_bap_stream_io_get_links(bap->stream),
> +                                       transport_desynchronize,
> +                                       UINT_TO_PTR(bap->desynchronized));
> +
> +       g_dbus_pending_property_success(id);
> +}
> +
>  static gboolean qos_ucast_exists(const GDBusPropertyTable *property, void *data)
>  {
>         struct media_transport *transport = data;
> @@ -1486,6 +1543,23 @@ static const GDBusPropertyTable transport_bap_uc_properties[] = {
>         { }
>  };
>
> +static const GDBusPropertyTable transport_bap_uc_test_properties[] = {
> +       { "Device", "o", get_device },
> +       { "UUID", "s", get_uuid },
> +       { "Codec", "y", get_codec },
> +       { "Configuration", "ay", get_configuration },
> +       { "State", "s", get_state },
> +       { "QoS", "a{sv}", get_ucast_qos, NULL, qos_ucast_exists },
> +       { "Endpoint", "o", get_endpoint, NULL, endpoint_exists },
> +       { "Location", "u", get_location },
> +       { "Metadata", "ay", get_metadata, set_metadata },
> +       { "Links", "ao", get_links, NULL, links_exists },
> +       { "Volume", "q", get_volume, set_volume, volume_exists },
> +       { "Desynchronized", "b", get_desynchronized, set_desynchronized,
> +                                                               links_exists},
> +       { }
> +};
> +
>  static gboolean get_bcast_qos(const GDBusPropertyTable *property,
>                                         DBusMessageIter *iter, void *data)
>  {
> @@ -1920,6 +1994,9 @@ static void transport_bap_update_links_uc(
>         g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
>                                                 MEDIA_TRANSPORT_INTERFACE,
>                                                 "Links");
> +       g_dbus_emit_property_changed(btd_get_dbus_connection(), transport->path,
> +                                               MEDIA_TRANSPORT_INTERFACE,
> +                                               "Desynchronized");
>
>         DBG("stream %p linked %s", bap->stream, bap->linked ? "true" : "false");
>  }
> @@ -2037,8 +2114,9 @@ static guint transport_bap_resume(struct media_transport *transport,
>                 return bap->resume_id;
>         }
>
> -       id = bt_bap_stream_enable(bap->stream, bap->linked, NULL,
> -                                       bap_enable_complete, owner);
> +       id = bt_bap_stream_enable(bap->stream,
> +                               bap->desynchronized ? false : bap->linked,
> +                               NULL, bap_enable_complete, owner);

I guess this is the main change that makes everything work together.
We do have a single user `bt_bap_stream_enable(stream, false)` in
test-bap but that doesn't seem to match this test case. Perhaps we
should add a test case for it under test-bap, doesn't need to be in
this set though.

>         if (!id)
>                 return 0;
>
> @@ -2161,7 +2239,7 @@ static void transport_bap_set_state(struct media_transport *transport,
>  {
>         struct bap_transport *bap = transport->data;
>
> -       if (!bap->linked)
> +       if (!bap->linked || bap->desynchronized)
>                 return;
>
>         /* Update links */
> @@ -2540,10 +2618,11 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
>  #define TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner, _init, \
>                       _resume, _suspend, _cancel, _set_state, _get_stream, \
>                       _get_volume, _set_volume, _set_delay, _update_links, \
> -                     _destroy) \
> +                     _destroy, _test_props) \
>  { \
>         .uuid = _uuid, \
>         .properties = _props, \
> +       .test_properties = _test_props, \
>         .set_owner = _set_owner, \
>         .remove_owner = _remove_owner, \
>         .init = _init, \
> @@ -2565,26 +2644,28 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
>                         transport_a2dp_resume, transport_a2dp_suspend, \
>                         transport_a2dp_cancel, NULL, \
>                         transport_a2dp_get_stream, transport_a2dp_get_volume, \
> -                       _set_volume, _set_delay, NULL, _destroy)
> +                       _set_volume, _set_delay, NULL, _destroy, NULL)
>
>  #define BAP_OPS(_uuid, _props, _set_owner, _remove_owner, _update_links, \
> -               _set_state) \
> +               _set_state, _test_props) \
>         TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner,\
>                         transport_bap_init, \
>                         transport_bap_resume, transport_bap_suspend, \
>                         transport_bap_cancel, _set_state, \
>                         transport_bap_get_stream, transport_bap_get_volume, \
>                         transport_bap_set_volume, NULL, \
> -                       _update_links, transport_bap_destroy)
> +                       _update_links, transport_bap_destroy, _test_props)
>
>  #define BAP_UC_OPS(_uuid) \
>         BAP_OPS(_uuid, transport_bap_uc_properties, \
>                         transport_bap_set_owner, transport_bap_remove_owner, \
> -                       transport_bap_update_links_uc, transport_bap_set_state)
> +                       transport_bap_update_links_uc, \
> +                       transport_bap_set_state, \
> +                       transport_bap_uc_test_properties)
>
>  #define BAP_BC_OPS(_uuid) \
>         BAP_OPS(_uuid, transport_bap_bc_properties, NULL, NULL, \
> -                       transport_bap_update_links_bc, NULL)
> +                       transport_bap_update_links_bc, NULL, NULL)
>
>  #define ASHA_OPS(_uuid) \
>         TRANSPORT_OPS(_uuid, transport_asha_properties, NULL, NULL, \
> @@ -2592,7 +2673,7 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
>                         transport_asha_resume, transport_asha_suspend, \
>                         transport_asha_cancel, NULL, NULL, \
>                         transport_asha_get_volume, transport_asha_set_volume, \
> -                       NULL, NULL, NULL)
> +                       NULL, NULL, NULL, NULL)
>
>  static const struct media_transport_ops transport_ops[] = {
>  #ifdef HAVE_A2DP
> @@ -2647,6 +2728,7 @@ struct media_transport *media_transport_create(struct btd_device *device,
>         struct media_transport *transport;
>         const struct media_transport_ops *ops;
>         int fd;
> +       const GDBusPropertyTable *properties;
>
>         transport = g_new0(struct media_transport, 1);
>         if (device)
> @@ -2701,9 +2783,14 @@ struct media_transport *media_transport_create(struct btd_device *device,
>                         goto fail;
>         }
>
> +       if (btd_opts.testing && ops->test_properties)
> +               properties = ops->test_properties;
> +       else
> +               properties = ops->properties;
> +
>         if (g_dbus_register_interface(btd_get_dbus_connection(),
>                                 transport->path, MEDIA_TRANSPORT_INTERFACE,
> -                               transport_methods, NULL, ops->properties,
> +                               transport_methods, NULL, properties,
>                                 transport, media_transport_free) == FALSE) {
>                 error("Could not register transport %s", transport->path);
>                 goto fail;
> --
> 2.43.0
>
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* RE: Add ability to desynchronized transports for PTS tests
From: bluez.test.bot @ 2026-04-27 18:56 UTC (permalink / raw)
  To: linux-bluetooth, frederic.danis
In-Reply-To: <20260427172056.148115-2-frederic.danis@collabora.com>

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

---Test result---

Test Summary:
CheckPatch                    FAIL      1.69 seconds
GitLint                       PASS      1.04 seconds
BuildEll                      PASS      20.25 seconds
BluezMake                     PASS      658.00 seconds
MakeCheck                     PASS      19.23 seconds
MakeDistcheck                 PASS      248.35 seconds
CheckValgrind                 PASS      297.60 seconds
CheckSmatch                   PASS      349.62 seconds
bluezmakeextell               PASS      181.86 seconds
IncrementalBuild              PASS      647.13 seconds
ScanBuild                     PASS      1011.48 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,2/3] doc: Add documentation for CIS transport Desynchronized property
WARNING:BAD_SIGN_OFF: Non-standard signature: Assisted-by:
#73: 
Assisted-by: GPT:GPT-5.3-Codex

ERROR:BAD_SIGN_OFF: Unrecognized email address: 'GPT:GPT-5.3-Codex'
#73: 
Assisted-by: GPT:GPT-5.3-Codex

/github/workspace/src/patch/14541954.patch total: 1 errors, 1 warnings, 17 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/14541954.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.




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

---
Regards,
Linux Bluetooth


^ permalink raw reply

* [Bug 221426] Intel AX211 Bluetooth hci0 hardware error 0x0c with HID/HOG failures on 6.19.14-zen
From: bugzilla-daemon @ 2026-04-27 19:12 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <bug-221426-62941@https.bugzilla.kernel.org/>

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

--- Comment #3 from Cristian Cocos (cristi@ieee.org) ---
(In reply to Paul Menzel from comment #2)
> Thank you for your report. Do you know if it’s a regression, that means, did
> it work with earlier Linux versions?

I do not recall this issue's happening with the previous Linux kernel
(6.19.13), hence very likely no regression.

> What firmware is used?

BlueZ 5.86, AX211 firmware 202-5.26

C

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* [PATCH BlueZ v1] monitor: Add decoding support for Public Broadcast Announcements
From: Luiz Augusto von Dentz @ 2026-04-27 19:22 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds support for decoding Public Broadcast Announcements (PBP).
---
 monitor/packet.c | 48 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/monitor/packet.c b/monitor/packet.c
index ff0b1cac2f5f..820191e0f7f5 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -3615,7 +3615,7 @@ static void print_ltv(const char *str, void *user_data)
 	print_field("%s: %s", label, str);
 }
 
-static void print_base_annoucement(const uint8_t *data, uint8_t data_len)
+static void print_base_announcement(const uint8_t *data, uint8_t data_len)
 {
 	struct iovec iov;
 	struct bt_hci_le_pa_base_data *base_data;
@@ -3714,7 +3714,7 @@ done:
 		print_hex_field("  Data", iov.iov_base, iov.iov_len);
 }
 
-static void print_broadcast_annoucement(const uint8_t *data, uint8_t data_len)
+static void print_broadcast_announcement(const uint8_t *data, uint8_t data_len)
 {
 	uint32_t bid;
 
@@ -3727,12 +3727,52 @@ static void print_broadcast_annoucement(const uint8_t *data, uint8_t data_len)
 	print_field("Broadcast ID: %u (0x%06x)", bid, bid);
 }
 
+static const struct bitfield_data pbp_feat_table[] = {
+	{ 0, "Encryption"					},
+	{ 1, "Standard Quality"					},
+	{ 2, "High Quality"					},
+	{}
+};
+
+static void print_pbp_announcement(const uint8_t *data, uint8_t data_len)
+{
+	struct iovec iov, meta;
+	uint8_t feat;
+	uint8_t len;
+
+	iov.iov_base = (void *) data;
+	iov.iov_len = data_len;
+
+	if (util_iov_pull_u8(&iov, &feat)) {
+		print_text(COLOR_ERROR, "  invalid features");
+		return;
+	}
+
+	print_field("Features: 0x%02x", feat);
+	print_bitfield(4, feat, pbp_feat_table);
+
+	if (util_iov_pull_u8(&iov, &len)) {
+		print_text(COLOR_ERROR, "  invalid metadata length");
+		return;
+	}
+
+	if (!len)
+		return;
+
+	meta.iov_base = util_iov_pull_mem(&iov, len);
+	meta.iov_len = len;
+
+	bt_bap_debug_metadata(meta.iov_base, meta.iov_len, print_ltv,
+						"    Metadata");
+}
+
 static const struct service_data_decoder {
 	uint16_t uuid;
 	void (*func)(const uint8_t *data, uint8_t data_len);
 } service_data_decoders[] = {
-	{ 0x1851, print_base_annoucement },
-	{ 0x1852, print_broadcast_annoucement }
+	{ 0x1851, print_base_announcement },
+	{ 0x1852, print_broadcast_announcement },
+	{ 0x1856, print_pbp_announcement }
 };
 
 static void print_service_data(const uint8_t *data, uint8_t data_len)
-- 
2.53.0


^ permalink raw reply related

* RE: Add RAS Packet format and Notification support
From: bluez.test.bot @ 2026-04-27 19:47 UTC (permalink / raw)
  To: linux-bluetooth, prathibha.madugonde
In-Reply-To: <20260427170007.1282289-2-prathm@qti.qualcomm.com>

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

---Test result---

Test Summary:
CheckPatch                    PASS      1.66 seconds
GitLint                       FAIL      0.49 seconds
BuildEll                      PASS      20.14 seconds
BluezMake                     PASS      648.66 seconds
MakeCheck                     PASS      0.95 seconds
MakeDistcheck                 PASS      244.58 seconds
CheckValgrind                 PASS      222.50 seconds
CheckSmatch                   PASS      348.86 seconds
bluezmakeextell               PASS      181.75 seconds
IncrementalBuild              FAIL      21.99 seconds
ScanBuild                     FAIL      35.95 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v4,1/3] src/shared: Add RAS packet formatting and notification support for CS reflector

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
1: T1 Title exceeds max length (94>80): "[BlueZ,v4,1/3] src/shared: Add RAS packet formatting and notification support for CS reflector"
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:

src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
[BlueZ,v4,1/3] src/shared: Add RAS packet formatting and notification support for CS reflector

src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4172: all] Error 2
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:

src/shared/gatt-client.c:447:21: warning: Use of memory after it is freed
        gatt_db_unregister(op->client->db, op->db_id);
                           ^~~~~~~~~~
src/shared/gatt-client.c:692:2: warning: Use of memory after it is freed
        discovery_op_complete(op, false, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:992:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1098:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1292:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1357:2: warning: Use of memory after it is freed
        discovery_op_complete(op, success, att_ecode);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1632:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1637:2: warning: Use of memory after it is freed
        discover_all(op);
        ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1693:56: warning: Use of memory after it is freed
        notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
                                          ~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2146:6: warning: Use of memory after it is freed
        if (read_db_hash(op)) {
            ^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2154:8: warning: Use of memory after it is freed
                                                        discovery_op_ref(op),
                                                        ^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3332:2: warning: Use of memory after it is freed
        complete_write_long_op(req, success, 0, false);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3354:2: warning: Use of memory after it is freed
        request_unref(req);
        ^~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/rap.c: In function ‘register_ras_service’:
src/shared/rap.c:968:3: error: implicit declaration of function ‘gatt_db_service_add_ccc_custom’; did you mean ‘gatt_db_service_add_ccc’? [-Werror=implicit-function-declaration]
  968 |   gatt_db_service_add_ccc_custom(ras->svc,
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |   gatt_db_service_add_ccc
src/shared/rap.c:967:25: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  967 |  ras->realtime_chrc_ccc =
      |                         ^
src/shared/rap.c:983:20: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  983 |  ras->ondemand_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                    ^
src/shared/rap.c:999:14: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
  999 |  ras->cp_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |              ^
src/shared/rap.c:1015:17: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1015 |  ras->ready_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                 ^
src/shared/rap.c:1031:23: error: assignment to ‘struct gatt_db_attribute *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
 1031 |  ras->overwritten_ccc = gatt_db_service_add_ccc_custom(ras->svc,
      |                       ^
cc1: all warnings being treated as errors
make[1]: *** [Makefile:7859: src/shared/libshared_mainloop_la-rap.lo] Error 1
make[1]: *** Waiting for unfinished jobs....
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
make: *** [Makefile:4172: all] Error 2


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

---
Regards,
Linux Bluetooth


^ permalink raw reply


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