public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* RE: Add initial Channel Sounding support for
  2026-04-17 13:56 [PATCH BlueZ v10 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
@ 2026-04-17 15:41 ` bluez.test.bot
  0 siblings, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2026-04-17 15:41 UTC (permalink / raw)
  To: linux-bluetooth, naga.akella

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

---Test result---

Test Summary:
CheckPatch                    PASS      2.53 seconds
GitLint                       FAIL      1.02 seconds
BuildEll                      PASS      20.24 seconds
BluezMake                     PASS      599.95 seconds
MakeCheck                     PASS      18.93 seconds
MakeDistcheck                 PASS      234.82 seconds
CheckValgrind                 PASS      275.82 seconds
CheckSmatch                   WARNING   321.10 seconds
bluezmakeextell               PASS      166.53 seconds
IncrementalBuild              PASS      637.08 seconds
ScanBuild                     PASS      926.72 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v10,1/3] shared: rap: Introduce Channel Sounding HCI raw interface support

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 (81>80): "[BlueZ,v10,1/3] shared: rap: Introduce Channel Sounding HCI raw interface support"
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/main.c: note: in included file (through src/device.h):


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

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v11 0/3] Add initial Channel Sounding support for
@ 2026-04-21 11:31 Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Naga Bhavani Akella @ 2026-04-21 11:31 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	prathibha.madugonde, Naga Bhavani Akella

This patch series introduces initial support for Bluetooth Channel
Sounding (CS) using the raw HCI interface.
This series lays the groundwork for CS support by adding commonly required
protocol definitions, configuration parsing, and event handling for
the reflector role.

The changes include:

  1) Introduction of raw HCI support structures and callbacks for Channel
     Sounding procedures and events. This provides the foundational data
     definitions and HCI subevent handling needed by higher-level profiles

  2) Addition of Channel Sounding configuration parsing from the BlueZ
     configuration file.This patch also updates the systemd
     service capability bounding set to include CAP_NET_RAW, which is
     required for bluetoothd to receive and process LE Channel Sounding
     events when running under a constrained systemd environment

  3) Implementation of HCI LE Channel Sounding event handling in the
     Ranging profile for the reflector role.
     This includes opening a raw HCI channel,
     parsing relevant CS LE events, routing them to the RAP profile

Patch overview:
 1/3 shared: rap: introduce Channel Sounding HCI raw interface support
 2/3 main.conf: add Channel Sounding config parsing support
 3/3 profiles: ranging: add HCI LE Channel Sounding event handling

Changes in v11:
- src/main.c - Fixed cast spacing (`(int8_t) tmp`)

- profiles/ranging/rap.c - Moved HCI state machine from adapter-level to
per-device level,
fixed cast spacing

- profiles/ranging/rap_hci.c - Fixed enum/struct naming
(removed `_t` suffix, added `CS_STATE_` prefix),
replaced indirect callback dispatch with direct function calls,
fixed cast spacing,
added blank lines before `if` statements, and removed double blank lines

Changes in v10:
- src/main.c: fixed parse_config_signed_int to use
`long` instead of `size_t` for signed ranges

- src/shared/rap.h: Updated bt_rap_attach_hci signature to
take CS options as parameters,
added bt_rap_get/set_hci_sm functions

- src/shared/rap.c: Added per-instance HCI state machine storage
with getter/setter functions

- profiles/ranging/rap.c: Updated to pass CS options directly to
bt_rap_attach_hci,
added NULL check for HCI device creation

- profiles/ranging/rap_hci.c: Converted global state machine and
cs_opt to per-instance,
added role_enable field to preserve HCI command values
separately from parsing values,
fixed C99 for-loop declarations,
made state_names and cs_callback_map static const,
replaced malloc with malloc0,
fixed off-by-one error in subevent length validation,
removed dead bt_hci_unref call


Naga Bhavani Akella (3):
  shared: rap: Introduce Channel Sounding HCI raw interface support
  main.conf: Add Channel Sounding config parsing  support
  profiles: ranging: Add HCI LE Event Handling in Reflector role

 Makefile.plugins           |    3 +-
 profiles/ranging/rap.c     |  190 +++++-
 profiles/ranging/rap_hci.c | 1259 ++++++++++++++++++++++++++++++++++++
 src/bluetooth.service.in   |    2 +-
 src/btd.h                  |    7 +
 src/main.c                 |  132 +++-
 src/main.conf              |   24 +
 src/shared/hci.c           |   62 +-
 src/shared/hci.h           |    3 +
 src/shared/rap.c           |   50 +-
 src/shared/rap.h           |  172 +++++
 11 files changed, 1881 insertions(+), 23 deletions(-)
 create mode 100644 profiles/ranging/rap_hci.c

-- 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support
  2026-04-21 11:31 [PATCH BlueZ v11 0/3] Add initial Channel Sounding support for Naga Bhavani Akella
@ 2026-04-21 11:31 ` Naga Bhavani Akella
  2026-04-21 12:48   ` Add initial Channel Sounding support for bluez.test.bot
  2026-04-21 11:31 ` [PATCH BlueZ v11 2/3] main.conf: Add Channel Sounding config parsing support Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role Naga Bhavani Akella
  2 siblings, 1 reply; 6+ messages in thread
From: Naga Bhavani Akella @ 2026-04-21 11:31 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	prathibha.madugonde, Naga Bhavani Akella

Implement stub callbacks for Channel Sounding HCI events and add the
required protocol definitions for CS configuration, procedure control,
and subevent result parsing

Add data structures to support Channel Sounding Processing
Add helper function to get hci conn info list and integrate it with RAP
---
 src/shared/hci.c |  62 ++++++++++++-----
 src/shared/hci.h |   3 +
 src/shared/rap.c |  50 +++++++++++++-
 src/shared/rap.h | 172 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 269 insertions(+), 18 deletions(-)

diff --git a/src/shared/hci.c b/src/shared/hci.c
index 575254c09..0faa6dea5 100644
--- a/src/shared/hci.c
+++ b/src/shared/hci.c
@@ -20,9 +20,11 @@
 #include <sys/un.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <fcntl.h>
 #include <errno.h>
 
+#include "bluetooth/hci.h"
 #include "monitor/bt.h"
 #include "src/shared/mainloop.h"
 #include "src/shared/io.h"
@@ -30,22 +32,6 @@
 #include "src/shared/queue.h"
 #include "src/shared/hci.h"
 
-#define BTPROTO_HCI	1
-struct sockaddr_hci {
-	sa_family_t	hci_family;
-	unsigned short	hci_dev;
-	unsigned short  hci_channel;
-};
-#define HCI_CHANNEL_RAW		0
-#define HCI_CHANNEL_USER	1
-
-#define SOL_HCI		0
-#define HCI_FILTER	2
-struct hci_filter {
-	uint32_t type_mask;
-	uint32_t event_mask[2];
-	uint16_t opcode;
-};
 
 struct bt_hci {
 	int ref_count;
@@ -673,3 +659,47 @@ bool bt_hci_unregister(struct bt_hci *hci, unsigned int id)
 
 	return true;
 }
+
+bool bt_hci_get_conn_handle(struct bt_hci *hci, const uint8_t *bdaddr,
+				uint16_t *handle)
+{
+	struct hci_conn_list_req *cl;
+	struct hci_conn_info *ci;
+	int fd, i;
+	bool found = false;
+
+	if (!hci || !bdaddr || !handle)
+		return false;
+
+	fd = io_get_fd(hci->io);
+	if (fd < 0)
+		return false;
+
+	/* Allocate buffer for connection list request */
+	cl = malloc(10 * sizeof(*ci) + sizeof(*cl));
+	if (!cl)
+		return false;
+
+	memset(cl, 0, 10 * sizeof(*ci) + sizeof(*cl));
+	cl->dev_id = 0;  /* Will be filled by ioctl */
+	cl->conn_num = 10;
+
+	/* Get connection list via ioctl */
+	if (ioctl(fd, HCIGETCONNLIST, (void *) cl) < 0) {
+		free(cl);
+		return false;
+	}
+
+	/* Search for the connection with matching bdaddr */
+	ci = cl->conn_info;
+	for (i = 0; i < cl->conn_num; i++, ci++) {
+		if (memcmp(&ci->bdaddr, bdaddr, 6) == 0) {
+			*handle = ci->handle;
+			found = true;
+			break;
+		}
+	}
+
+	free(cl);
+	return found;
+}
diff --git a/src/shared/hci.h b/src/shared/hci.h
index 76ee72f54..800dc4946 100644
--- a/src/shared/hci.h
+++ b/src/shared/hci.h
@@ -41,3 +41,6 @@ unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
 				bt_hci_callback_func_t callback,
 				void *user_data, bt_hci_destroy_func_t destroy);
 bool bt_hci_unregister(struct bt_hci *hci, unsigned int id);
+
+bool bt_hci_get_conn_handle(struct bt_hci *hci, const uint8_t *bdaddr,
+				uint16_t *handle);
diff --git a/src/shared/rap.c b/src/shared/rap.c
index ccf3e6f33..ac6de04e0 100644
--- a/src/shared/rap.c
+++ b/src/shared/rap.c
@@ -25,8 +25,8 @@
 #include "src/shared/gatt-client.h"
 #include "src/shared/rap.h"
 
-#define DBG(_rap, fmt, arg...) \
-	rap_debug(_rap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
+#define DBG(_rap, fmt, ...) \
+	rap_debug(_rap, "%s:%s() " fmt, __FILE__, __func__, ##__VA_ARGS__)
 
 #define RAS_UUID16			0x185B
 
@@ -503,6 +503,52 @@ bool bt_rap_unregister(unsigned int id)
 	return true;
 }
 
+void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
+						const void *param,
+						void *user_data)
+{
+	struct bt_rap *rap = user_data;
+
+	DBG(rap, "Received CS subevent CONT: len=%d", length);
+}
+
+void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
+					const void *param,
+					void *user_data)
+{
+	struct bt_rap *rap = user_data;
+
+	DBG(rap, "Received CS subevent: len=%d", length);
+}
+
+void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
+						const void *param,
+						void *user_data)
+{
+	struct bt_rap *rap = user_data;
+
+	DBG(rap, "Received CS procedure enable complete subevent: len=%d",
+	    length);
+}
+
+void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
+						 const void *param,
+						 void *user_data)
+{
+	struct bt_rap *rap = user_data;
+
+	DBG(rap, "Received CS security enable subevent: len=%d", length);
+}
+
+void bt_rap_hci_cs_config_complete_callback(uint16_t length,
+					const void *param,
+					void *user_data)
+{
+	struct bt_rap *rap = user_data;
+
+	DBG(rap, "Received CS config complete subevent: len=%d", length);
+}
+
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb)
 {
 	struct bt_rap *rap;
diff --git a/src/shared/rap.h b/src/shared/rap.h
index a1d1ff2ae..15ddea295 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -9,8 +9,153 @@
 #include <inttypes.h>
 
 #include "src/shared/io.h"
+#include "bluetooth/mgmt.h"
+#include "src/shared/hci.h"
 
 struct bt_rap;
+struct gatt_db;
+struct bt_gatt_client;
+
+/* Channel Sounding Events */
+struct bt_rap_hci_cs_options {
+	uint8_t role;
+	uint8_t cs_sync_ant_sel;
+	int8_t max_tx_power;
+	int rtt_type;
+};
+
+#define CS_MODE_ZERO				0x00
+#define CS_MODE_ONE				0x01
+#define CS_MODE_TWO				0x02
+#define CS_MODE_THREE				0x03
+
+#define CS_REFLECTOR			0x01
+#define CS_INITIATOR			0x00
+
+#define CS_MAX_ANT_PATHS			0x05
+#define CS_MAX_STEPS			0xA0
+#define CS_MAX_STEP_DATA_LEN		0xFF
+
+struct rap_ev_cs_config_cmplt {
+	uint8_t status;
+	uint16_t conn_hdl;
+	uint8_t config_id;
+	uint8_t action;
+	uint8_t main_mode_type;
+	uint8_t sub_mode_type;
+	uint8_t min_main_mode_steps;
+	uint8_t max_main_mode_steps;
+	uint8_t main_mode_rep;
+	uint8_t mode_0_steps;
+	uint8_t role;
+	uint8_t rtt_type;
+	uint8_t cs_sync_phy;
+	uint8_t channel_map[10];
+	uint8_t channel_map_rep;
+	uint8_t channel_sel_type;
+	uint8_t ch3c_shape;
+	uint8_t ch3c_jump;
+	uint8_t reserved;
+	uint8_t t_ip1_time;
+	uint8_t t_ip2_time;
+	uint8_t t_fcs_time;
+	uint8_t t_pm_time;
+};
+
+struct rap_ev_cs_sec_enable_cmplt {
+	uint8_t status;
+	uint16_t conn_hdl;
+};
+
+struct rap_ev_cs_proc_enable_cmplt {
+	uint8_t status;
+	uint16_t conn_hdl;
+	uint8_t config_id;
+	uint8_t state;
+	uint8_t tone_ant_config_sel;
+	int8_t sel_tx_pwr;
+	uint8_t sub_evt_len[3];
+	uint8_t sub_evts_per_evt;
+	uint16_t sub_evt_intrvl;
+	uint16_t evt_intrvl;
+	uint16_t proc_intrvl;
+	uint16_t proc_counter;
+	uint16_t max_proc_len;
+};
+
+struct pct_iq_sample {
+	int16_t i_sample;
+	int16_t q_sample;
+};
+
+struct cs_mode_zero_data {
+	uint8_t packet_quality;
+	uint8_t packet_rssi_dbm;
+	uint8_t packet_ant;
+	uint32_t init_measured_freq_offset;
+};
+
+struct cs_mode_one_data {
+	uint8_t packet_quality;
+	uint8_t packet_rssi_dbm;
+	uint8_t packet_ant;
+	uint8_t packet_nadm;
+	int16_t toa_tod_init;
+	int16_t tod_toa_refl;
+	struct pct_iq_sample packet_pct1;
+	struct pct_iq_sample packet_pct2;
+};
+
+struct cs_mode_two_data {
+	uint8_t ant_perm_index;
+	struct pct_iq_sample tone_pct[5];
+	uint8_t tone_quality_indicator[5];
+};
+
+struct cs_mode_three_data {
+	struct cs_mode_one_data mode_one_data;
+	struct cs_mode_two_data mode_two_data;
+};
+
+union cs_mode_data {
+	struct cs_mode_zero_data mode_zero_data;
+	struct cs_mode_one_data mode_one_data;
+	struct cs_mode_two_data mode_two_data;
+	struct cs_mode_three_data mode_three_data;
+};
+
+struct cs_step_data {
+	uint8_t step_mode;
+	uint8_t step_chnl;
+	uint8_t step_data_length;
+	union cs_mode_data step_mode_data;
+};
+
+struct rap_ev_cs_subevent_result {
+	uint16_t conn_hdl;
+	uint8_t config_id;
+	uint16_t start_acl_conn_evt_counter;
+	uint16_t proc_counter;
+	uint16_t freq_comp;
+	uint8_t ref_pwr_lvl;
+	uint8_t proc_done_status;
+	uint8_t subevt_done_status;
+	uint8_t abort_reason;
+	uint8_t num_ant_paths;
+	uint8_t num_steps_reported;
+	struct cs_step_data step_data[];
+};
+
+struct rap_ev_cs_subevent_result_cont {
+	uint16_t conn_hdl;
+	uint8_t config_id;
+	uint8_t proc_done_status;
+	uint8_t subevt_done_status;
+	uint8_t abort_reason;
+	uint8_t num_ant_paths;
+	uint8_t num_steps_reported;
+	struct cs_step_data step_data[];
+};
 
 typedef void (*bt_rap_debug_func_t)(const char *str, void *user_data);
 typedef void (*bt_rap_ready_func_t)(struct bt_rap *rap, void *user_data);
@@ -43,3 +188,30 @@ bool bt_rap_ready_unregister(struct bt_rap *rap, unsigned int id);
 bool bt_rap_unregister(unsigned int id);
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb);
+
+/* HCI Raw Channel Approach */
+void bt_rap_hci_cs_config_complete_callback(uint16_t length,
+					     const void *param,
+					     void *user_data);
+void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
+						 const void *param,
+						 void *user_data);
+void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
+						      const void *param,
+						      void *user_data);
+void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
+					     const void *param,
+					     void *user_data);
+void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
+						  const void *param,
+						  void *user_data);
+
+void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
+			uint8_t role, uint8_t cs_sync_ant_sel,
+			int8_t max_tx_power);
+void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
+
+/* Connection handle mapping functions */
+bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
+				const uint8_t *bdaddr, uint8_t bdaddr_type);
+void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
-- 


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v11 2/3] main.conf: Add Channel Sounding config parsing  support
  2026-04-21 11:31 [PATCH BlueZ v11 0/3] Add initial Channel Sounding support for Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
@ 2026-04-21 11:31 ` Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role Naga Bhavani Akella
  2 siblings, 0 replies; 6+ messages in thread
From: Naga Bhavani Akella @ 2026-04-21 11:31 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	prathibha.madugonde, Naga Bhavani Akella

Add support for parsing Channel Sounding (CS) configuration options
from the configuration file.

Add CAP_NET_RAW to CapabilityBoundingSet in bluetooth.service.
bluetoothd requires CAP_NET_RAW to receive and process HCI LE events
when running under a constrained systemd capability bounding set
---
 src/bluetooth.service.in |   2 +-
 src/btd.h                |   7 +++
 src/main.c               | 132 ++++++++++++++++++++++++++++++++++++++-
 src/main.conf            |  24 +++++++
 4 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
index 8ebe89bec..8dcbde236 100644
--- a/src/bluetooth.service.in
+++ b/src/bluetooth.service.in
@@ -10,7 +10,7 @@ ExecStart=@PKGLIBEXECDIR@/bluetoothd
 NotifyAccess=main
 #WatchdogSec=10
 #Restart=on-failure
-CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_NET_BIND_SERVICE
 LimitNPROC=1
 
 # Filesystem lockdown
diff --git a/src/btd.h b/src/btd.h
index c84a600d1..db2e81239 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -94,11 +94,18 @@ struct btd_le_defaults {
 	uint8_t		enable_advmon_interleave_scan;
 };
 
+struct btd_le_bcs {
+	uint8_t role;
+	uint8_t cs_sync_ant_sel;
+	int8_t max_tx_power;
+};
+
 struct btd_defaults {
 	uint16_t	num_entries;
 
 	struct btd_br_defaults br;
 	struct btd_le_defaults le;
+	struct btd_le_bcs bcs;
 };
 
 struct btd_csis {
diff --git a/src/main.c b/src/main.c
index 818f7c06e..9a3d2da25 100644
--- a/src/main.c
+++ b/src/main.c
@@ -156,6 +156,13 @@ static const char *gatt_options[] = {
 	NULL
 };
 
+static const char *const bcs_options[] = {
+	"Role",
+	"CsSyncAntennaSel",
+	"MaxTxPower",
+	NULL
+};
+
 static const char *csip_options[] = {
 	"SIRK",
 	"Encryption",
@@ -183,7 +190,7 @@ static const char *advmon_options[] = {
 
 static const struct group_table {
 	const char *name;
-	const char **options;
+	const char * const *options;
 } valid_groups[] = {
 	{ "General",	supported_options },
 	{ "BR",		br_options },
@@ -193,6 +200,7 @@ static const struct group_table {
 	{ "CSIS",	csip_options },
 	{ "AVDTP",	avdtp_options },
 	{ "AVRCP",	avrcp_options },
+	{ "ChannelSounding",	bcs_options },
 	{ "AdvMon",	advmon_options },
 	{ }
 };
@@ -356,7 +364,7 @@ static enum jw_repairing_t parse_jw_repairing(const char *jw_repairing)
 
 
 static void check_options(GKeyFile *config, const char *group,
-						const char **options)
+						const char * const *options)
 {
 	char **keys;
 	int i;
@@ -492,6 +500,46 @@ static bool parse_config_int(GKeyFile *config, const char *group,
 	return true;
 }
 
+static bool parse_config_signed_int(GKeyFile *config, const char *group,
+					const char *key, int8_t *val,
+					long min, long max)
+{
+	char *str = NULL;
+	char *endptr = NULL;
+	long tmp;
+	bool result = false;
+
+	str = g_key_file_get_string(config, group, key, NULL);
+	if (!str)
+		return false;
+
+	tmp = strtol(str, &endptr, 0);
+	if (!endptr || *endptr != '\0') {
+		warn("%s.%s = %s is not integer", group, key, str);
+		goto cleanup;
+	}
+
+	if (tmp < min) {
+		warn("%s.%s = %ld is out of range (< %ld)", group, key, tmp,
+			min);
+		goto cleanup;
+	}
+
+	if (tmp > max) {
+		warn("%s.%s = %ld is out of range (> %ld)", group, key, tmp,
+									max);
+		goto cleanup;
+	}
+
+	if (val)
+		*val = (int8_t) tmp;
+	result = true;
+
+cleanup:
+	g_free(str);
+	return result;
+}
+
 struct config_param {
 	const char * const val_name;
 	void * const val;
@@ -1184,6 +1232,81 @@ static void parse_csis(GKeyFile *config)
 					0, UINT8_MAX);
 }
 
+static bool parse_cs_role(GKeyFile *config, const char *group,
+					const char *key, uint8_t *val)
+{
+	GError *err = NULL;
+	char *str = NULL;
+	char *endptr = NULL;
+	int numeric_val;
+
+	/* Try to read as string first */
+	str = g_key_file_get_string(config, group, key, &err);
+	if (err) {
+		if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND)
+			DBG("%s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	DBG("%s.%s = %s", group, key, str);
+
+	/* Check if it's a string value */
+	if (!strcmp(str, "Initiator") || !strcmp(str, "initiator")) {
+		if (val)
+			*val = 1;
+		g_free(str);
+		return true;
+	} else if (!strcmp(str, "Reflector") || !strcmp(str, "reflector")) {
+		if (val)
+			*val = 2;
+		g_free(str);
+		return true;
+	} else if (!strcmp(str, "Both") || !strcmp(str, "both")) {
+		if (val)
+			*val = 3;
+		g_free(str);
+		return true;
+	}
+
+	/* Try to parse as numeric value */
+	numeric_val = strtol(str, &endptr, 0);
+
+	if (!endptr || *endptr != '\0') {
+		warn("%s.%s = %s is not a valid value. "
+			"Expected: 1/Initiator, 2/Reflector, or 3/Both",
+			group, key, str);
+		g_free(str);
+		return false;
+	}
+
+	if (numeric_val < 1 || numeric_val > 3) {
+		warn("%s.%s = %d is out of range. "
+			"Valid values: 1 (Initiator), 2 (Reflector), 3 (Both)",
+			group, key, numeric_val);
+		g_free(str);
+		return false;
+	}
+
+	if (val)
+		*val = numeric_val;
+
+	g_free(str);
+	return true;
+}
+
+static void parse_le_cs_config(GKeyFile *config)
+{
+	parse_cs_role(config, "ChannelSounding", "Role",
+			&btd_opts.defaults.bcs.role);
+	parse_config_u8(config, "ChannelSounding", "CsSyncAntennaSel",
+			&btd_opts.defaults.bcs.cs_sync_ant_sel,
+			0x01, 0xFF);
+	parse_config_signed_int(config, "ChannelSounding",
+			"MaxTxPower", &btd_opts.defaults.bcs.max_tx_power,
+			INT8_MIN, INT8_MAX);
+}
+
 static void parse_avdtp_session_mode(GKeyFile *config)
 {
 	char *str = NULL;
@@ -1262,6 +1385,7 @@ static void parse_config(GKeyFile *config)
 	parse_csis(config);
 	parse_avdtp(config);
 	parse_avrcp(config);
+	parse_le_cs_config(config);
 	parse_advmon(config);
 }
 
@@ -1313,6 +1437,10 @@ static void init_defaults(void)
 
 	btd_opts.advmon.rssi_sampling_period = 0xFF;
 	btd_opts.csis.encrypt = true;
+
+	btd_opts.defaults.bcs.role = 0x03;
+	btd_opts.defaults.bcs.cs_sync_ant_sel = 0xFF;
+	btd_opts.defaults.bcs.max_tx_power = 0x14;
 }
 
 static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
diff --git a/src/main.conf b/src/main.conf
index d31dd1b8f..5846ef92d 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -299,6 +299,30 @@
 # Default = auto
 # Security = auto
 
+[ChannelSounding]
+# Current role of the device
+# Possible values:
+#   1 or "Initiator" - CS Initiator role,
+#   Generally, CS Initiator acts as Client (Gatt role) and Central (Gap role)
+#   2 or "Reflector" - CS Reflector role,
+#   Generally, CS Reflector acts as Server (Gatt role) and Peripheral (Gap role)
+#   3 or "Both"      - Both Initiator and Reflector roles
+# Default: 3 (Both)
+#Role = 3
+
+# Antenna Identifier to be used
+# Possible values:
+# 0x01-0x04 (antenna identifier to be used),
+# 0xFE - Antennas to be used in repetitive order,
+# 0xFF - Host doesn't have recommendation
+# Default: 0xFF (Host doesn't have recommendation)
+#CsSyncAntennaSel = 0xFF
+
+# Maximum Transmit power
+# Possible values: 0x81-0x14 (-127dBm to 20dBm)
+# Default: 0x14 (Max Power possible)
+#MaxTxPower = 0x14
+
 [CSIS]
 # SIRK - Set Identification Resolution Key which is common for all the
 # sets. They SIRK key is used to identify its sets. This can be any
-- 


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH BlueZ v11 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role
  2026-04-21 11:31 [PATCH BlueZ v11 0/3] Add initial Channel Sounding support for Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
  2026-04-21 11:31 ` [PATCH BlueZ v11 2/3] main.conf: Add Channel Sounding config parsing support Naga Bhavani Akella
@ 2026-04-21 11:31 ` Naga Bhavani Akella
  2 siblings, 0 replies; 6+ messages in thread
From: Naga Bhavani Akella @ 2026-04-21 11:31 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	prathibha.madugonde, Naga Bhavani Akella

Open RAW HCI Channel for CS Event Handling
Parse the following HCI LE CS Events in reflector role
and route the events to RAP Profile.
 1. HCI_EVT_LE_CS_READ_RMT_SUPP_CAP_COMPLETE
 2. HCI_EVT_LE_CS_CONFIG_COMPLETE
 3. HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE
 4. HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE
 5. HCI_EVT_LE_CS_SUBEVENT_RESULT
 6. HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE
Send HCI_OP_LE_CS_SET_DEFAULT_SETTINGS to the controller
with default settings selected by the user.
Map connection handle received to device connection
---
 Makefile.plugins           |    3 +-
 profiles/ranging/rap.c     |  190 +++++-
 profiles/ranging/rap_hci.c | 1259 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1450 insertions(+), 2 deletions(-)
 create mode 100644 profiles/ranging/rap_hci.c

diff --git a/Makefile.plugins b/Makefile.plugins
index c9efadb45..ac667beda 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -89,7 +89,8 @@ builtin_modules += battery
 builtin_sources += profiles/battery/battery.c
 
 builtin_modules += rap
-builtin_sources += profiles/ranging/rap.c
+builtin_sources += profiles/ranging/rap.c \
+		profiles/ranging/rap_hci.c
 
 if SIXAXIS
 builtin_modules += sixaxis
diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
index f03454c72..df4f07811 100644
--- a/profiles/ranging/rap.c
+++ b/profiles/ranging/rap.c
@@ -17,6 +17,7 @@
 #include "gdbus/gdbus.h"
 
 #include "bluetooth/bluetooth.h"
+#include "bluetooth/l2cap.h"
 #include "bluetooth/uuid.h"
 
 #include "src/plugin.h"
@@ -34,15 +35,131 @@
 #include "src/shared/rap.h"
 #include "attrib/att.h"
 #include "src/log.h"
+#include "src/btd.h"
+
+struct rap_adapter_data {
+	struct btd_adapter *adapter;
+	struct bt_hci *hci;  /* Shared HCI raw channel */
+	int ref_count;  /* Number of devices using this adapter */
+};
 
 struct rap_data {
 	struct btd_device *device;
 	struct btd_service *service;
 	struct bt_rap *rap;
 	unsigned int ready_id;
+	struct rap_adapter_data *adapter_data;  /* Shared adapter-level HCI */
+	void *hci_sm;  /* Per-device HCI state machine */
 };
 
 static struct queue *sessions;
+static struct queue *adapter_list;  /* List of rap_adapter_data */
+
+/* Adapter data management */
+static bool match_adapter(const void *data, const void *match_data)
+{
+	const struct rap_adapter_data *adapter_data = data;
+	const struct btd_adapter *adapter = match_data;
+
+	return adapter_data->adapter == adapter;
+}
+
+static struct rap_adapter_data *rap_adapter_data_find(
+		struct btd_adapter *adapter)
+{
+	if (!adapter_list)
+		return NULL;
+
+	return queue_find(adapter_list, match_adapter, adapter);
+}
+
+static struct rap_adapter_data *rap_adapter_data_new(
+		struct btd_adapter *adapter)
+{
+	struct rap_adapter_data *adapter_data;
+	int16_t hci_index;
+
+	hci_index = btd_adapter_get_index(adapter);
+	DBG("Creating new adapter_data for hci%d", hci_index);
+
+	adapter_data = new0(struct rap_adapter_data, 1);
+	if (!adapter_data) {
+		error("Failed to allocate adapter_data");
+		return NULL;
+	}
+
+	adapter_data->adapter = adapter;
+	adapter_data->ref_count = 0;
+
+	/* Create adapter list if needed */
+	if (!adapter_list) {
+		DBG("Creating new adapter_list");
+		adapter_list = queue_new();
+	}
+
+	/* Add to queue BEFORE creating HCI to prevent race condition */
+	queue_push_tail(adapter_list, adapter_data);
+	DBG("Added adapter_data to queue");
+
+	/* Create HCI raw channel for this adapter */
+	DBG("Opening HCI raw device for hci%d", hci_index);
+	adapter_data->hci = bt_hci_new_raw_device(hci_index);
+
+	if (!adapter_data->hci) {
+		error("Failed to create HCI raw device for hci%d", hci_index);
+		queue_remove(adapter_list, adapter_data);
+		free(adapter_data);
+		return NULL;
+	}
+
+	DBG("HCI raw channel created successfully for hci%d", hci_index);
+
+	return adapter_data;
+}
+
+static struct rap_adapter_data *rap_adapter_data_ref(
+		struct btd_adapter *adapter)
+{
+	struct rap_adapter_data *adapter_data;
+
+	adapter_data = rap_adapter_data_find(adapter);
+	if (!adapter_data) {
+		adapter_data = rap_adapter_data_new(adapter);
+		if (!adapter_data)
+			return NULL;
+	}
+
+	adapter_data->ref_count++;
+
+	return adapter_data;
+}
+
+static void rap_adapter_data_unref(struct rap_adapter_data *adapter_data)
+{
+	if (!adapter_data)
+		return;
+
+	adapter_data->ref_count--;
+
+	if (adapter_data->ref_count > 0)
+		return;
+
+	/* No more devices using this adapter, clean up */
+	DBG("Cleaning up adapter HCI channel");
+
+	if (adapter_data->hci) {
+		bt_hci_unref(adapter_data->hci);
+		adapter_data->hci = NULL;
+	}
+
+	queue_remove(adapter_list, adapter_data);
+	free(adapter_data);
+
+	if (queue_isempty(adapter_list)) {
+		queue_destroy(adapter_list, NULL);
+		adapter_list = NULL;
+	}
+}
 
 static struct rap_data *rap_data_new(struct btd_device *device)
 {
@@ -95,6 +212,19 @@ static void rap_data_free(struct rap_data *data)
 	}
 
 	bt_rap_ready_unregister(data->rap, data->ready_id);
+
+	/* Detach per-device HCI state machine */
+	if (data->hci_sm) {
+		bt_rap_detach_hci(data->rap, data->hci_sm);
+		data->hci_sm = NULL;
+	}
+
+	/* Release reference to shared adapter HCI channel */
+	if (data->adapter_data) {
+		rap_adapter_data_unref(data->adapter_data);
+		data->adapter_data = NULL;
+	}
+
 	bt_rap_unref(data->rap);
 	free(data);
 }
@@ -177,7 +307,7 @@ static int rap_probe(struct btd_service *service)
 	ba2str(device_get_address(device), addr);
 	DBG("%s", addr);
 
-	/*Ignore, if we probed for this device already */
+	/* Ignore, if we probed for this device already */
 	if (data) {
 		error("Profile probed twice for this device");
 		return -EINVAL;
@@ -195,6 +325,35 @@ static int rap_probe(struct btd_service *service)
 		return -EINVAL;
 	}
 
+	/* Get or create shared adapter-level HCI channel */
+	data->adapter_data = rap_adapter_data_ref(adapter);
+	if (!data->adapter_data) {
+		error("Failed to get adapter HCI channel");
+		bt_rap_unref(data->rap);
+		free(data);
+		return -EINVAL;
+	}
+
+	DBG("Using shared HCI channel for adapter (ref_count=%d)",
+		data->adapter_data->ref_count);
+
+	/* Create per-device HCI state machine with valid rap instance */
+	DBG("Attaching per-device HCI state machine");
+	data->hci_sm = bt_rap_attach_hci(data->rap, data->adapter_data->hci,
+					btd_opts.defaults.bcs.role,
+					btd_opts.defaults.bcs.cs_sync_ant_sel,
+					btd_opts.defaults.bcs.max_tx_power);
+
+	if (!data->hci_sm) {
+		error("Failed to attach HCI state machine for device");
+		rap_adapter_data_unref(data->adapter_data);
+		bt_rap_unref(data->rap);
+		free(data);
+		return -EINVAL;
+	}
+
+	DBG("HCI state machine attached successfully for device");
+
 	rap_data_add(data);
 
 	data->ready_id = bt_rap_ready_register(data->rap, rap_ready, service,
@@ -228,6 +387,10 @@ static int rap_accept(struct btd_service *service)
 	struct btd_device *device = btd_service_get_device(service);
 	struct bt_gatt_client *client = btd_device_get_gatt_client(device);
 	struct rap_data *data = btd_service_get_user_data(service);
+	struct bt_att *att;
+	const bdaddr_t *bdaddr;
+	uint8_t bdaddr_type;
+	uint16_t handle;
 	char addr[18];
 
 	ba2str(device_get_address(device), addr);
@@ -243,6 +406,31 @@ static int rap_accept(struct btd_service *service)
 		return -EINVAL;
 	}
 
+	/* Set up connection handle mapping for CS event routing */
+	att = bt_rap_get_att(data->rap);
+	bdaddr = device_get_address(device);
+	bdaddr_type = device_get_le_address_type(device);
+
+	if (att && data->adapter_data && data->adapter_data->hci &&
+	    data->hci_sm) {
+		/* Use bt_hci_get_conn_handle to find the connection handle
+		 * by bdaddr using HCIGETCONNLIST ioctl
+		 */
+		if (bt_hci_get_conn_handle(data->adapter_data->hci,
+					(const uint8_t *) bdaddr, &handle)) {
+			DBG("Found conn handle 0x%04X for %s", handle, addr);
+			DBG("Setting up handle mapping: handle=0x%04X",
+				handle);
+			bt_rap_set_conn_handle(data->hci_sm,
+						data->rap, handle,
+						(const uint8_t *) bdaddr,
+						bdaddr_type);
+		} else {
+			error("Failed to find connection handle for device %s",
+				addr);
+		}
+	}
+
 	btd_service_connecting_complete(service, 0);
 
 	return 0;
diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
new file mode 100644
index 000000000..1aca4bbf8
--- /dev/null
+++ b/profiles/ranging/rap_hci.c
@@ -0,0 +1,1259 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <endian.h>
+
+#include "lib/bluetooth/bluetooth.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/rap.h"
+#include "src/log.h"
+#include "monitor/bt.h"
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Macro to sign-extend an N-bit value to 16-bit signed integer */
+#define SIGN_EXTEND_TO_16(val, bits) \
+	((int16_t)(((val) ^ (1U << ((bits)-1))) - (1U << ((bits)-1))))
+
+/*  CS State Definitions */
+enum cs_state {
+	CS_STATE_INIT,
+	CS_STATE_STOPPED,
+	CS_STATE_STARTED,
+	CS_STATE_WAIT_CONFIG_CMPLT,
+	CS_STATE_WAIT_SEC_CMPLT,
+	CS_STATE_WAIT_PROC_CMPLT,
+	CS_STATE_HOLD,
+	CS_STATE_UNSPECIFIED
+};
+
+static const char * const state_names[] = {
+	"CS_STATE_INIT",
+	"CS_STATE_STOPPED",
+	"CS_STATE_STARTED",
+	"CS_STATE_WAIT_CONFIG_CMPLT",
+	"CS_STATE_WAIT_SEC_CMPLT",
+	"CS_STATE_WAIT_PROC_CMPLT",
+	"CS_STATE_HOLD",
+	"CS_STATE_UNSPECIFIED"
+};
+
+/* Callback Function Type */
+typedef void (*cs_callback_t)(uint16_t length,
+			const void *param, void *user_data);
+
+/* State Machine Context */
+struct cs_state_machine {
+	enum cs_state current_state;
+	enum cs_state old_state;
+	struct bt_hci *hci;
+	struct bt_rap *rap;
+	unsigned int event_id;
+	bool initiator;
+	bool procedure_active;
+	struct bt_rap_hci_cs_options cs_opt;  /* Per-instance CS options */
+	uint8_t role_enable;  /* Role value for HCI commands (1, 2, or 3) */
+	struct queue *conn_mappings;  /* Per-instance connection mappings */
+};
+
+/* Connection Handle Mapping */
+struct rap_conn_mapping {
+	uint16_t handle;
+	uint8_t bdaddr[6];
+	uint8_t bdaddr_type;
+	struct bt_att *att;
+	struct bt_rap *rap;
+};
+
+/* Connection Mapping Helper Functions */
+static void mapping_free(void *data)
+{
+	struct rap_conn_mapping *mapping = data;
+
+	if (!mapping)
+		return;
+
+	free(mapping);
+}
+
+static bool match_mapping_handle(const void *a, const void *b)
+{
+	const struct rap_conn_mapping *mapping = a;
+	uint16_t handle = PTR_TO_UINT(b);
+
+	return mapping->handle == handle;
+}
+
+static bool match_mapping_rap(const void *a, const void *b)
+{
+	const struct rap_conn_mapping *mapping = a;
+	const struct bt_rap *rap = b;
+
+	return mapping->rap == rap;
+}
+
+static struct rap_conn_mapping *find_mapping_by_handle(
+					struct cs_state_machine *sm,
+					uint16_t handle)
+{
+	if (!sm || !sm->conn_mappings)
+		return NULL;
+
+	return queue_find(sm->conn_mappings, match_mapping_handle,
+				UINT_TO_PTR(handle));
+}
+
+static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
+				const uint8_t *bdaddr, uint8_t bdaddr_type,
+				struct bt_att *att, struct bt_rap *rap)
+{
+	struct rap_conn_mapping *mapping;
+
+	if (!sm)
+		return false;
+
+	if (!sm->conn_mappings) {
+		sm->conn_mappings = queue_new();
+		if (!sm->conn_mappings)
+			return false;
+	}
+
+	/* Check if mapping already exists */
+	mapping = find_mapping_by_handle(sm, handle);
+	if (mapping) {
+		/* Update existing mapping */
+		if (bdaddr)
+			memcpy(mapping->bdaddr, bdaddr, 6);
+		mapping->bdaddr_type = bdaddr_type;
+		mapping->att = att;
+		mapping->rap = rap;
+		return true;
+	}
+
+	/* Create new mapping */
+	mapping = new0(struct rap_conn_mapping, 1);
+	if (!mapping)
+		return false;
+
+	mapping->handle = handle;
+	if (bdaddr)
+		memcpy(mapping->bdaddr, bdaddr, 6);
+	mapping->bdaddr_type = bdaddr_type;
+	mapping->att = att;
+	mapping->rap = rap;
+
+	return queue_push_tail(sm->conn_mappings, mapping);
+}
+
+static void remove_conn_mapping(struct cs_state_machine *sm, uint16_t handle)
+{
+	struct rap_conn_mapping *mapping;
+
+	if (!sm || !sm->conn_mappings)
+		return;
+
+	mapping = queue_remove_if(sm->conn_mappings, match_mapping_handle,
+					UINT_TO_PTR(handle));
+	if (mapping)
+		mapping_free(mapping);
+}
+
+static void remove_rap_mappings(struct cs_state_machine *sm)
+{
+	if (!sm || !sm->conn_mappings)
+		return;
+
+	queue_remove_all(sm->conn_mappings, match_mapping_rap, sm->rap,
+				mapping_free);
+}
+
+static struct bt_rap *resolve_handle_to_rap(struct cs_state_machine *sm,
+						uint16_t handle)
+{
+	struct rap_conn_mapping *mapping;
+
+	if (!sm)
+		return NULL;
+
+	/* Try to find in mapping cache */
+	mapping = find_mapping_by_handle(sm, handle);
+	if (mapping && mapping->rap) {
+		DBG("Found handle 0x%04X in mapping cache", handle);
+		return mapping->rap;
+	}
+
+	/* Profile layer should have called bt_rap_set_conn_handle() during
+	 * connection establishment. If we reach here, the mapping was not set.
+	 */
+	DBG("No mapping found for handle 0x%04X", handle);
+	DBG("Profile layer should call bt_rap_set_conn_handle() on connect");
+
+	return NULL;
+}
+
+/*  State Machine Functions */
+static void cs_state_machine_init(struct cs_state_machine *sm,
+				struct bt_rap *rap, struct bt_hci *hci,
+				uint8_t role, uint8_t cs_sync_ant_sel,
+				int8_t max_tx_power)
+{
+	if (!sm)
+		return;
+
+	sm->current_state = CS_STATE_UNSPECIFIED;
+	sm->rap = rap;
+	sm->hci = hci;
+	sm->initiator = false;
+	sm->procedure_active = false;
+
+	/* Store role_enable for HCI commands (1, 2, or 3 from config) */
+	sm->role_enable = role;
+
+	/* Initialize per-instance CS options
+	 * Note: cs_opt.role will be overwritten with actual role (0x00 or 0x01)
+	 * from config complete event, but role_enable preserves the HCI value
+	 */
+	sm->cs_opt.role = role;
+	sm->cs_opt.cs_sync_ant_sel = cs_sync_ant_sel;
+	sm->cs_opt.max_tx_power = max_tx_power;
+	sm->cs_opt.rtt_type = 0;  /* Will be set from config complete event */
+}
+
+/* State Transition Logic */
+static void cs_set_state(struct cs_state_machine *sm,
+		enum cs_state new_state)
+{
+	if (!sm)
+		return;
+
+	if (sm->current_state == new_state)
+		return;
+
+	/* Validate state values before array access */
+	if (sm->current_state > CS_STATE_UNSPECIFIED ||
+	    new_state > CS_STATE_UNSPECIFIED) {
+		error("Invalid state transition attempted");
+		return;
+	}
+
+	DBG("[STATE] Transition: %s → %s",
+		state_names[sm->current_state],
+		state_names[new_state]);
+
+	sm->old_state = sm->current_state;
+	sm->current_state = new_state;
+}
+
+static enum cs_state cs_get_current_state(struct cs_state_machine *sm)
+{
+	return sm ? sm->current_state : CS_STATE_UNSPECIFIED;
+}
+
+/* HCI Event Callbacks */
+static void rap_def_settings_done_cb(const void *data, uint8_t size,
+					void *user_data)
+{
+	struct bt_hci_rsp_le_cs_set_def_settings *rp;
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_rsp_le_cs_set_def_settings))
+		return;
+
+	DBG("size=0x%02X", size);
+
+	rp = (struct bt_hci_rsp_le_cs_set_def_settings *) data;
+
+	if (cs_get_current_state(sm) != CS_STATE_INIT) {
+		DBG("Event received in Wrong State!! Expected : CS_STATE_INIT");
+		return;
+	}
+
+	if (rp->status == 0) {
+		/* Success - proceed to configuration */
+		cs_set_state(sm, CS_STATE_WAIT_CONFIG_CMPLT);
+
+		/* Reflector role */
+		DBG("Waiting for CS Config Completed event...");
+		/* TODO: Initiator role - Send CS Config complete cmd */
+	} else {
+		/* Error - transition to stopped */
+		error("CS Set default setting failed with status 0x%02X",
+		rp->status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+	}
+}
+
+static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
+		struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *ev)
+{
+	struct bt_hci_cmd_le_cs_set_def_settings cp;
+	unsigned int status;
+
+	memset(&cp, 0, sizeof(cp));
+
+	if (!sm || !sm->hci) {
+		error("Set Def Settings: sm or hci is null");
+		return;
+	}
+
+	if (ev->handle)
+		cp.handle = ev->handle;
+
+	cp.role_enable = sm->role_enable;  /* Use preserved HCI command value */
+	cp.cs_sync_antenna_selection = sm->cs_opt.cs_sync_ant_sel;
+	cp.max_tx_power = sm->cs_opt.max_tx_power;
+
+	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_DEF_SETTINGS,
+				&cp, sizeof(cp), rap_def_settings_done_cb,
+				sm, NULL);
+
+	DBG("sending set default settings case, status : %d", status);
+
+	if (!status)
+		error("Failed to send default settings cmd");
+}
+
+static void rap_rd_rmt_supp_cap_cmplt_evt(const uint8_t *data, uint8_t size,
+					   void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *evt;
+	struct bt_rap *rap;
+	struct iovec iov;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	/* Pull the entire structure at once */
+	evt = util_iov_pull_mem(&iov, sizeof(*evt));
+
+	if (!evt) {
+		error("Failed to pull remote cap complete struct");
+		return;
+	}
+
+	DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
+
+	/* Check status */
+	if (evt->status != 0) {
+		error("Remote capabilities failed with status 0x%02X",
+			evt->status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	/* Resolve handle to RAP instance */
+	rap = resolve_handle_to_rap(sm, evt->handle);
+
+	if (!rap) {
+		DBG("[WARN] Could not resolve handle 0x%04X to RAP instance",
+			evt->handle);
+		/* Continue with state machine RAP for now */
+		rap = sm->rap;
+	}
+
+	DBG("num_config=%u, ",
+		evt->num_config_supported);
+	DBG("max_consecutive_proc=%u, num_antennas=%u, ",
+		evt->max_consecutive_procedures_supported,
+		evt->num_antennas_supported);
+	DBG("max_antenna_paths=%u, roles=0x%02X, modes=0x%02X",
+		evt->max_antenna_paths_supported,
+		evt->roles_supported,
+		evt->modes_supported);
+
+	rap_send_hci_def_settings_command(sm,
+		(struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *) evt);
+	cs_set_state(sm, CS_STATE_INIT);
+}
+
+static void rap_cs_config_cmplt_evt(const uint8_t *data, uint8_t size,
+				    void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	const struct bt_hci_evt_le_cs_config_complete *evt;
+	struct rap_ev_cs_config_cmplt rap_ev;
+	struct iovec iov;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_config_complete))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	DBG("size=0x%02X", size);
+
+	/* State Check */
+	if (cs_get_current_state(sm) != CS_STATE_WAIT_CONFIG_CMPLT) {
+		DBG("Event received in Wrong State!! ");
+		DBG("Expected : CS_STATE_WAIT_CONFIG_CMPLT");
+		return;
+	}
+
+	/* Pull the entire structure at once */
+	evt = util_iov_pull_mem(&iov, sizeof(*evt));
+	if (!evt) {
+		error("Failed to pull config complete struct");
+		return;
+	}
+
+	DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
+
+	/* Check status */
+	if (evt->status != 0) {
+		error("Configuration failed with status 0x%02X",
+			evt->status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+		return;
+	}
+
+	/* Copy fields to rap_ev structure */
+	rap_ev.status = evt->status;
+	rap_ev.conn_hdl = cpu_to_le16(evt->handle);
+	rap_ev.config_id = evt->config_id;
+	rap_ev.action = evt->action;
+	rap_ev.main_mode_type = evt->main_mode_type;
+	rap_ev.sub_mode_type = evt->sub_mode_type;
+	rap_ev.min_main_mode_steps = evt->min_main_mode_steps;
+	rap_ev.max_main_mode_steps = evt->max_main_mode_steps;
+	rap_ev.main_mode_rep = evt->main_mode_repetition;
+	rap_ev.mode_0_steps = evt->mode_0_steps;
+	rap_ev.role = evt->role;
+	rap_ev.rtt_type = evt->rtt_type;
+	rap_ev.cs_sync_phy = evt->cs_sync_phy;
+	memcpy(rap_ev.channel_map, evt->channel_map, 10);
+	rap_ev.channel_map_rep = evt->channel_map_repetition;
+	rap_ev.channel_sel_type = evt->channel_selection_type;
+	rap_ev.ch3c_shape = evt->ch3c_shape;
+	rap_ev.ch3c_jump = evt->ch3c_jump;
+	rap_ev.reserved = evt->reserved;
+	rap_ev.t_ip1_time = evt->t_ip1_time;
+	rap_ev.t_ip2_time = evt->t_ip2_time;
+	rap_ev.t_fcs_time = evt->t_fcs_time;
+	rap_ev.t_pm_time = evt->t_pm_time;
+
+	/* Store role and rtt_type from config complete event
+	 * Note: evt->role contains actual role
+	 * (CS_INITIATOR=0x00, CS_REFLECTOR=0x01)
+	 * which is different from the role_enable value sent in HCI command
+	 */
+	sm->cs_opt.role = rap_ev.role;
+	sm->cs_opt.rtt_type = rap_ev.rtt_type;
+
+	DBG("config_id=%u, action=%u, ",
+		rap_ev.config_id, rap_ev.action);
+	DBG("main_mode=%u, sub_mode=%u, role=%u, rtt_type=%u",
+		rap_ev.main_mode_type, rap_ev.sub_mode_type,
+		rap_ev.role, rap_ev.rtt_type);
+
+	/* Success - proceed to Security enable complete */
+	cs_set_state(sm, CS_STATE_WAIT_SEC_CMPLT);
+
+	/* Reflector role */
+	DBG("Waiting for security enable event...");
+	/* TODO: Initiator role - Send CS Security enable cmd */
+
+	/* Send callback to RAP Profile */
+	bt_rap_hci_cs_config_complete_callback(size, &rap_ev, sm->rap);
+}
+
+static void rap_cs_sec_enable_cmplt_evt(const uint8_t *data, uint8_t size,
+					 void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	struct rap_ev_cs_sec_enable_cmplt rap_ev;
+	struct iovec iov;
+	uint8_t status;
+	uint16_t handle;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_sec_enable_complete))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	DBG("size=0x%02X", size);
+
+	/* State Check */
+	if (cs_get_current_state(sm) != CS_STATE_WAIT_SEC_CMPLT) {
+		DBG("Event received in Wrong State!! ");
+		DBG("Expected : CS_STATE_WAIT_SEC_CMPLT");
+		return;
+	}
+
+	/* Parse all fields in order using iovec */
+	if (!util_iov_pull_u8(&iov, &status)) {
+		error("Failed to parse Status");
+		return;
+	}
+
+	if (!util_iov_pull_le16(&iov, &handle)) {
+		error("Failed to parse Connection_Handle");
+		return;
+	}
+
+	rap_ev.status = status;
+	rap_ev.conn_hdl = cpu_to_le16(handle);
+
+	DBG("status=0x%02X, handle=0x%04X",
+		rap_ev.status, handle);
+
+	if (rap_ev.status == 0) {
+		/* Success - proceed to configuration */
+		cs_set_state(sm, CS_STATE_WAIT_PROC_CMPLT);
+
+		/* Reflector role */
+		DBG("Waiting for CS Proc complete event...");
+		/* TODO: Initiator - Send CS Proc Set Parameter and enable */
+	} else {
+		/* Error - transition to stopped */
+		error("Security enable failed with status 0x%02X",
+			rap_ev.status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+	}
+
+	/* Send callback to RAP Profile */
+	bt_rap_hci_cs_sec_enable_complete_callback(size, &rap_ev, sm->rap);
+}
+
+static void rap_cs_proc_enable_cmplt_evt(const uint8_t *data, uint8_t size,
+					  void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	const struct bt_hci_evt_le_cs_proc_enable_complete *evt;
+	struct rap_ev_cs_proc_enable_cmplt rap_ev;
+	struct iovec iov;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_proc_enable_complete))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	DBG("size=0x%02X", size);
+
+	/* State Check */
+	if (cs_get_current_state(sm) != CS_STATE_WAIT_PROC_CMPLT) {
+		DBG("Event received in Wrong State!! ");
+		DBG("Expected : CS_STATE_WAIT_PROC_CMPLT");
+		return;
+	}
+
+	/* Pull the entire structure at once */
+	evt = util_iov_pull_mem(&iov, sizeof(*evt));
+	if (!evt) {
+		error("Failed to pull proc enable complete struct");
+		return;
+	}
+
+	DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
+
+	/* Check status */
+	if (evt->status != 0) {
+		error("Procedure enable failed with status 0x%02X",
+			evt->status);
+		cs_set_state(sm, CS_STATE_STOPPED);
+		sm->procedure_active = false;
+		return;
+	}
+
+	/* Copy fields to rap_ev structure */
+	rap_ev.status = evt->status;
+	rap_ev.conn_hdl = cpu_to_le16(evt->handle);
+	rap_ev.config_id = evt->config_id;
+	rap_ev.state = evt->state;
+	rap_ev.tone_ant_config_sel = evt->tone_antenna_config_selection;
+	rap_ev.sel_tx_pwr = evt->selected_tx_power;
+	memcpy(rap_ev.sub_evt_len, evt->subevent_len, 3);
+	rap_ev.sub_evts_per_evt = evt->subevents_per_event;
+	rap_ev.sub_evt_intrvl = evt->subevent_interval;
+	rap_ev.evt_intrvl = evt->event_interval;
+	rap_ev.proc_intrvl = evt->procedure_interval;
+	rap_ev.proc_counter = evt->procedure_count;
+	rap_ev.max_proc_len = evt->max_procedure_len;
+
+	DBG("config_id=%u, state=%u, ",
+		rap_ev.config_id, rap_ev.state);
+	DBG("sub_evts_per_evt=%u, evt_intrvl=%u, proc_intrvl=%u",
+		rap_ev.sub_evts_per_evt, rap_ev.evt_intrvl,
+		rap_ev.proc_intrvl);
+
+	/* Success - procedure started */
+	cs_set_state(sm, CS_STATE_STARTED);
+	sm->procedure_active = true;
+
+	/* Send callback to RAP Profile */
+	bt_rap_hci_cs_procedure_enable_complete_callback(size,
+			&rap_ev, sm->rap);
+}
+
+static void parse_i_q_sample(struct iovec *iov, int16_t *i_sample,
+				int16_t *q_sample)
+{
+	uint32_t buffer;
+	uint32_t i12;
+	uint32_t q12;
+
+	/* Pull 24-bit little-endian value from iovec */
+	if (!util_iov_pull_le24(iov, &buffer)) {
+		*i_sample = 0;
+		*q_sample = 0;
+		return;
+	}
+
+	/* Extract 12-bit I and Q values from 24-bit buffer */
+	i12 =  buffer        & 0x0FFFU;   /* bits 0..11 */
+	q12 = (buffer >> 12) & 0x0FFFU;   /* bits 12..23 */
+
+	/* Sign-extend 12-bit values to 16-bit using macro */
+	*i_sample = SIGN_EXTEND_TO_16(i12, 12);
+	*q_sample = SIGN_EXTEND_TO_16(q12, 12);
+}
+
+/* Parse CS Mode 0 step data */
+static void parse_mode_zero_data(struct iovec *iov,
+				 struct cs_mode_zero_data *mode_data,
+				 uint8_t cs_role)
+{
+	uint32_t freq_offset;
+
+	if (iov->iov_len < 3) {
+		DBG("Mode 0: too short (<3)");
+		return;
+	}
+
+	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);
+	DBG("CS Step mode 0");
+
+	if (cs_role == CS_INITIATOR && iov->iov_len >= 4) {
+		util_iov_pull_le32(iov, &freq_offset);
+		mode_data->init_measured_freq_offset = freq_offset;
+	}
+}
+
+/* Parse CS Mode 1 step data */
+static void parse_mode_one_data(struct iovec *iov,
+				struct cs_mode_one_data *mode_data,
+				uint8_t cs_role, uint8_t cs_rtt_type)
+{
+	uint16_t time_val;
+
+	if (iov->iov_len < 4) {
+		DBG("Mode 1: too short (<4)");
+		return;
+	}
+
+	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;
+	}
+
+	if ((cs_rtt_type == 0x01 || cs_rtt_type == 0x02) &&
+		iov->iov_len >= 6) {
+		int16_t i_val, q_val;
+
+		parse_i_q_sample(iov, &i_val, &q_val);
+		mode_data->packet_pct1.i_sample = i_val;
+		mode_data->packet_pct1.q_sample = q_val;
+
+		parse_i_q_sample(iov, &i_val, &q_val);
+		mode_data->packet_pct2.i_sample = i_val;
+		mode_data->packet_pct2.q_sample = q_val;
+	}
+}
+
+/* Parse CS Mode 2 step data */
+static void parse_mode_two_data(struct iovec *iov,
+				struct cs_mode_two_data *mode_data,
+				uint8_t max_paths)
+{
+	uint8_t k;
+
+	if (iov->iov_len < 1) {
+		DBG("Mode 2: too short (<1)");
+		return;
+	}
+
+	util_iov_pull_u8(iov, &mode_data->ant_perm_index);
+	DBG("CS Step mode 2, max paths : %d", max_paths);
+
+	for (k = 0; k < max_paths; k++) {
+		int16_t i_val, q_val;
+
+		if (iov->iov_len < 4) {
+			DBG("Mode 2: insufficient PCT for path %u (rem=%zu)",
+				k, iov->iov_len);
+			break;
+		}
+		parse_i_q_sample(iov, &i_val, &q_val);
+		mode_data->tone_pct[k].i_sample = i_val;
+		mode_data->tone_pct[k].q_sample = q_val;
+
+		util_iov_pull_u8(iov, &mode_data->tone_quality_indicator[k]);
+		DBG("tone_quality_indicator : %d",
+			mode_data->tone_quality_indicator[k]);
+		DBG("[i, q] : %d, %d",
+			mode_data->tone_pct[k].i_sample,
+			mode_data->tone_pct[k].q_sample);
+	}
+}
+
+/* Parse CS Mode 3 step data */
+static void parse_mode_three_data(struct iovec *iov,
+				struct cs_mode_three_data *mode_data,
+				uint8_t cs_role, uint8_t cs_rtt_type,
+				uint8_t max_paths)
+{
+	uint8_t k;
+	struct cs_mode_one_data *mode_one = &mode_data->mode_one_data;
+	struct cs_mode_two_data *mode_two = &mode_data->mode_two_data;
+
+	if (iov->iov_len < 4) {
+		DBG("Mode 3: mode1 too short (<4)");
+		return;
+	}
+
+	DBG("CS Step mode 3");
+
+	/* Parse Mode 1 portion */
+	parse_mode_one_data(iov, mode_one, cs_role, cs_rtt_type);
+
+	/* Parse Mode 2 portion */
+	if (iov->iov_len >= 1) {
+		util_iov_pull_u8(iov, &mode_two->ant_perm_index);
+		for (k = 0; k < max_paths; k++) {
+			int16_t i_val, q_val;
+
+			if (iov->iov_len < 4)
+				break;
+			parse_i_q_sample(iov, &i_val, &q_val);
+			mode_two->tone_pct[k].i_sample = i_val;
+			mode_two->tone_pct[k].q_sample = q_val;
+
+			util_iov_pull_u8(iov,
+					 &mode_two->tone_quality_indicator[k]);
+		}
+	}
+}
+
+/* Parse a single CS step */
+static void parse_cs_step(struct iovec *iov, struct cs_step_data *step,
+			uint8_t cs_role, uint8_t cs_rtt_type,
+			uint8_t max_paths)
+{
+	uint8_t mode;
+	uint8_t chnl;
+	uint8_t length;
+
+	/* Check if we have enough data for the 3-byte header */
+	if (iov->iov_len < 3) {
+		DBG("Truncated header for step");
+		return;
+	}
+
+	/* Read mode, channel, and length (3-byte header) */
+	if (!util_iov_pull_u8(iov, &mode) ||
+		!util_iov_pull_u8(iov, &chnl) ||
+		!util_iov_pull_u8(iov, &length)) {
+		DBG("Failed to read header for step");
+		return;
+	}
+
+	DBG("event->step_data_len : %d", length);
+
+	step->step_mode = mode;
+	step->step_chnl = chnl;
+	step->step_data_length = length;
+
+	DBG("Step: mode=%u chnl=%u data_len=%u", mode, chnl, length);
+
+	if (iov->iov_len < length) {
+		DBG("Truncated payload for step (need %u, have %zu)",
+			length, iov->iov_len);
+		return;
+	}
+
+	/* Parse step data based on mode */
+	switch (mode) {
+	case CS_MODE_ZERO:
+		parse_mode_zero_data(iov, &step->step_mode_data.mode_zero_data,
+					cs_role);
+		break;
+	case CS_MODE_ONE:
+		parse_mode_one_data(iov, &step->step_mode_data.mode_one_data,
+					cs_role, cs_rtt_type);
+		break;
+	case CS_MODE_TWO:
+		parse_mode_two_data(iov, &step->step_mode_data.mode_two_data,
+					max_paths);
+		break;
+	case CS_MODE_THREE:
+		parse_mode_three_data(iov,
+					&step->step_mode_data.mode_three_data,
+					cs_role, cs_rtt_type, max_paths);
+		break;
+	default:
+		DBG("Unknown step mode %d", mode);
+		/* Skip the entire step data */
+		util_iov_pull(iov, length);
+		break;
+	}
+}
+
+static void rap_cs_subevt_result_evt(const uint8_t *data, uint8_t size,
+				void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	struct rap_ev_cs_subevent_result *rap_ev;
+	struct iovec iov;
+	uint8_t cs_role;
+	uint8_t cs_rtt_type;
+	uint8_t max_paths;
+	uint8_t steps;
+	size_t send_len = 0;
+	uint16_t handle;
+	uint8_t config_id;
+	uint16_t start_acl_conn_evt_counter;
+	uint16_t proc_counter;
+	uint16_t freq_comp;
+	uint8_t ref_pwr_lvl;
+	uint8_t proc_done_status;
+	uint8_t subevt_done_status;
+	uint8_t abort_reason;
+	uint8_t num_ant_paths;
+	uint8_t num_steps_reported;
+	uint8_t i;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_subevent_result))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	/* Check if Procedure is active or not */
+	if (!sm->procedure_active) {
+		DBG("Received Subevent event when Procedure is inactive!");
+		return;
+	}
+
+	/* Parse header fields using iovec */
+	if (!util_iov_pull_le16(&iov, &handle)) {
+		error("Failed to parse Connection_Handle");
+		return;
+	}
+
+	if (!util_iov_pull_u8(&iov, &config_id) ||
+		!util_iov_pull_le16(&iov, &start_acl_conn_evt_counter) ||
+		!util_iov_pull_le16(&iov, &proc_counter) ||
+		!util_iov_pull_le16(&iov, &freq_comp) ||
+		!util_iov_pull_u8(&iov, &ref_pwr_lvl) ||
+		!util_iov_pull_u8(&iov, &proc_done_status) ||
+		!util_iov_pull_u8(&iov, &subevt_done_status) ||
+		!util_iov_pull_u8(&iov, &abort_reason) ||
+		!util_iov_pull_u8(&iov, &num_ant_paths) ||
+		!util_iov_pull_u8(&iov, &num_steps_reported)) {
+		error("Failed to parse subevent fields");
+		return;
+	}
+
+	cs_role = sm->cs_opt.role;
+	cs_rtt_type = sm->cs_opt.rtt_type;
+	max_paths = MIN((num_ant_paths + 1), CS_MAX_ANT_PATHS);
+	steps = MIN(num_steps_reported, CS_MAX_STEPS);
+	send_len = offsetof(struct rap_ev_cs_subevent_result, step_data) +
+					steps * sizeof(struct cs_step_data);
+	rap_ev = malloc0(send_len);
+	if (!rap_ev) {
+		error("Failed to allocate memory for subevent result\n");
+		return;
+	}
+
+	DBG("length=%u", size);
+	rap_ev->conn_hdl                     = le16_to_cpu(handle);
+	rap_ev->config_id                    = config_id;
+	rap_ev->start_acl_conn_evt_counter   = start_acl_conn_evt_counter;
+	rap_ev->proc_counter                 = proc_counter;
+	rap_ev->freq_comp                    = freq_comp;
+	rap_ev->ref_pwr_lvl                  = ref_pwr_lvl;
+	rap_ev->proc_done_status             = proc_done_status;
+	rap_ev->subevt_done_status           = subevt_done_status;
+	rap_ev->abort_reason                 = abort_reason;
+	rap_ev->num_ant_paths                = num_ant_paths;
+	rap_ev->num_steps_reported           = steps;
+
+	if (num_steps_reported > CS_MAX_STEPS) {
+		DBG("Too many steps reported: %u (max %u)",
+			num_steps_reported, CS_MAX_STEPS);
+		goto send_event;
+	}
+
+	/* Early exit for error conditions */
+	if (rap_ev->subevt_done_status == 0xF ||
+	    rap_ev->proc_done_status == 0xF) {
+		DBG("CS Procedure/Subevent aborted: ");
+		DBG("sub evt status = %d, proc status = %d, reason = %d",
+			rap_ev->subevt_done_status, rap_ev->proc_done_status,
+			rap_ev->abort_reason);
+		goto send_event;
+	}
+
+	/* Parse interleaved step data from remaining iovec data */
+	for (i = 0; i < steps; i++)
+		parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
+			max_paths);
+
+send_event:
+	DBG("CS subevent result processed: %zu bytes, ", send_len);
+	bt_rap_hci_cs_subevent_result_callback(send_len, rap_ev, sm->rap);
+	free(rap_ev);
+}
+
+static void rap_cs_subevt_result_cont_evt(const uint8_t *data, uint8_t size,
+					void *user_data)
+{
+	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+	struct rap_ev_cs_subevent_result_cont *rap_ev;
+	struct iovec iov;
+	uint8_t cs_role;
+	uint8_t cs_rtt_type;
+	uint8_t max_paths;
+	uint8_t steps;
+	size_t send_len = 0;
+	uint16_t handle;
+	uint8_t config_id;
+	uint8_t proc_done_status;
+	uint8_t subevt_done_status;
+	uint8_t abort_reason;
+	uint8_t num_ant_paths;
+	uint8_t num_steps_reported;
+	uint8_t i;
+
+	if (!sm || !data ||
+		size < sizeof(struct bt_hci_evt_le_cs_subevent_result_continue))
+		return;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	/* Check if Procedure is active or not */
+	if (!sm->procedure_active) {
+		error("Received Subevent when CS Procedure is inactive!");
+		return;
+	}
+
+	/* Parse header fields using iovec */
+	if (!util_iov_pull_le16(&iov, &handle)) {
+		error("Failed to parse Connection_Handle");
+		return;
+	}
+
+	if (!util_iov_pull_u8(&iov, &config_id) ||
+		!util_iov_pull_u8(&iov, &proc_done_status) ||
+		!util_iov_pull_u8(&iov, &subevt_done_status) ||
+		!util_iov_pull_u8(&iov, &abort_reason) ||
+		!util_iov_pull_u8(&iov, &num_ant_paths) ||
+		!util_iov_pull_u8(&iov, &num_steps_reported)) {
+		error("Failed to parse subevent continue fields ");
+		return;
+	}
+
+	cs_role = sm->cs_opt.role;
+	cs_rtt_type = sm->cs_opt.rtt_type;
+	max_paths = MIN((num_ant_paths + 1), CS_MAX_ANT_PATHS);
+	steps = MIN(num_steps_reported, CS_MAX_STEPS);
+	send_len = offsetof(struct rap_ev_cs_subevent_result_cont, step_data) +
+					steps * sizeof(struct cs_step_data);
+	rap_ev = malloc0(send_len);
+	if (!rap_ev) {
+		error("Failed to allocate memory for subevent result\n");
+		return;
+	}
+
+	DBG("length=%u", size);
+	rap_ev->conn_hdl                     = le16_to_cpu(handle);
+	rap_ev->config_id                    = config_id;
+	rap_ev->proc_done_status             = proc_done_status;
+	rap_ev->subevt_done_status           = subevt_done_status;
+	rap_ev->abort_reason                 = abort_reason;
+	rap_ev->num_ant_paths                = num_ant_paths;
+	rap_ev->num_steps_reported           = steps;
+
+	if (num_steps_reported > CS_MAX_STEPS) {
+		DBG("Too many steps reported: %u (max %u)",
+			num_steps_reported, CS_MAX_STEPS);
+		goto send_event;
+	}
+
+	/* Early exit for error conditions */
+	if (rap_ev->subevt_done_status == 0xF ||
+	    rap_ev->proc_done_status == 0xF) {
+		DBG("CS Procedure/Subevent aborted: ");
+		DBG("sub evt status = %d, proc status = %d, reason = %d",
+			rap_ev->subevt_done_status, rap_ev->proc_done_status,
+			rap_ev->abort_reason);
+		goto send_event;
+	}
+
+	/* Parse interleaved step data from remaining iovec data */
+	for (i = 0; i < steps; i++)
+		parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
+			max_paths);
+
+send_event:
+	DBG("CS subevent result cont processed: %zu bytes, ", send_len);
+	bt_rap_hci_cs_subevent_result_cont_callback(send_len, rap_ev, sm->rap);
+	free(rap_ev);
+}
+
+/* Subevent handler function type */
+typedef void (*subevent_handler_t)(const uint8_t *data, uint8_t size,
+				   void *user_data);
+
+/* Subevent table entry */
+struct subevent_entry {
+	uint8_t opcode;
+	uint8_t min_len;
+	uint8_t max_len;
+	subevent_handler_t handler;
+	const char *name;
+};
+
+/* Macro to define HCI event entries
+ * Note: min_len excludes the subevent byte since it's stripped before dispatch
+ */
+#define HCI_EVT(_opcode, _struct, _handler, _name) \
+	{ \
+		.opcode = _opcode, \
+		.min_len = sizeof(_struct), \
+		.max_len = 0xFF, \
+		.handler = _handler, \
+		.name = _name \
+	}
+
+/* Subevent dispatch table */
+static const struct subevent_entry subevent_table[] = {
+	HCI_EVT(BT_HCI_EVT_LE_CS_RD_REM_SUPP_CAP_COMPLETE,
+		struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete,
+		rap_rd_rmt_supp_cap_cmplt_evt,
+		"CS Read Remote Supported Capabilities Complete"),
+	HCI_EVT(BT_HCI_EVT_LE_CS_CONFIG_COMPLETE,
+		struct bt_hci_evt_le_cs_config_complete,
+		rap_cs_config_cmplt_evt,
+		"CS Config Complete"),
+	HCI_EVT(BT_HCI_EVT_LE_CS_SEC_ENABLE_COMPLETE,
+		struct bt_hci_evt_le_cs_sec_enable_complete,
+		rap_cs_sec_enable_cmplt_evt,
+		"CS Security Enable Complete"),
+	HCI_EVT(BT_HCI_EVT_LE_CS_PROC_ENABLE_COMPLETE,
+		struct bt_hci_evt_le_cs_proc_enable_complete,
+		rap_cs_proc_enable_cmplt_evt,
+		"CS Procedure Enable Complete"),
+	HCI_EVT(BT_HCI_EVT_LE_CS_SUBEVENT_RESULT,
+		struct bt_hci_evt_le_cs_subevent_result,
+		rap_cs_subevt_result_evt,
+		"CS Subevent Result"),
+	HCI_EVT(BT_HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE,
+		struct bt_hci_evt_le_cs_subevent_result_continue,
+		rap_cs_subevt_result_cont_evt,
+		"CS Subevent Result Continue")
+};
+
+#undef HCI_EVT
+
+#define SUBEVENT_TABLE_SIZE ARRAY_SIZE(subevent_table)
+
+/* HCI Event Registration */
+static void rap_handle_hci_events(const void *data, uint8_t size,
+				void *user_data)
+{
+	struct iovec iov;
+	uint8_t subevent;
+	const struct subevent_entry *entry = NULL;
+	size_t i;
+
+	/* Initialize iovec with the event data */
+	iov.iov_base = (void *) data;
+	iov.iov_len = size;
+
+	/* Pull the subevent code */
+	if (!util_iov_pull_u8(&iov, &subevent)) {
+		DBG("Failed to parse subevent code");
+		return;
+	}
+
+	/* Find the subevent in the table */
+	for (i = 0; i < SUBEVENT_TABLE_SIZE; i++) {
+		if (subevent_table[i].opcode == subevent) {
+			entry = &subevent_table[i];
+			break;
+		}
+	}
+
+	/* Check if subevent is supported */
+	if (!entry) {
+		DBG("Unknown subevent: 0x%02X", subevent);
+		return;
+	}
+
+	/* Validate payload length */
+	if (iov.iov_len < entry->min_len) {
+		DBG("%s: payload too short (%zu < %u)",
+		    entry->name, iov.iov_len, entry->min_len);
+		return;
+	}
+
+	if (entry->max_len != 0xFF && iov.iov_len > entry->max_len) {
+		DBG("%s: payload too long (%zu > %u)",
+		    entry->name, iov.iov_len, entry->max_len);
+		return;
+	}
+
+	/* Call the handler */
+	DBG("Handling %s (opcode=0x%02X, len=%zu)",
+	    entry->name, subevent, iov.iov_len);
+
+	entry->handler(iov.iov_base, iov.iov_len, user_data);
+}
+
+void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
+			uint8_t role, uint8_t cs_sync_ant_sel,
+			int8_t max_tx_power)
+{
+	struct cs_state_machine *sm;
+
+	if (!rap || !hci) {
+		error("rap or hci null");
+		return NULL;
+	}
+
+	/* Allocate per-instance state machine */
+	sm = new0(struct cs_state_machine, 1);
+	if (!sm) {
+		error("Failed to allocate state machine");
+		return NULL;
+	}
+
+	/* Initialize state machine with provided CS options */
+	cs_state_machine_init(sm, rap, hci, role, cs_sync_ant_sel,
+				max_tx_power);
+
+	sm->event_id = bt_hci_register(hci, BT_HCI_EVT_LE_META_EVENT,
+					rap_handle_hci_events, sm, NULL);
+
+	DBG("bt_hci_register done, event_id : %d", sm->event_id);
+
+	if (!sm->event_id) {
+		error("Failed to register hci le meta events");
+		error("event_id=0x%02X", sm->event_id);
+		free(sm);
+		return NULL;
+	}
+
+	DBG("CS options: role=%u, cs_sync_ant_sel=%u, max_tx_power=%d",
+		role, cs_sync_ant_sel, max_tx_power);
+
+	return sm;
+}
+
+bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
+				const uint8_t *bdaddr, uint8_t bdaddr_type)
+{
+	struct cs_state_machine *sm = hci_sm;
+	struct bt_att *att;
+
+	if (!sm || !rap)
+		return false;
+
+	att = bt_rap_get_att(rap);
+	if (!att)
+		return false;
+
+	DBG("Setting connection mapping: handle=0x%04X, ", handle);
+	if (bdaddr) {
+		DBG("bdaddr=%02x:%02x:%02x:%02x:%02x:%02x type=%u",
+			bdaddr[5], bdaddr[4], bdaddr[3],
+			bdaddr[2], bdaddr[1], bdaddr[0], bdaddr_type);
+	}
+
+	return add_conn_mapping(sm, handle, bdaddr, bdaddr_type, att, rap);
+}
+
+void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm)
+		return;
+
+	DBG("Clearing connection mapping: handle=0x%04X", handle);
+	remove_conn_mapping(sm, handle);
+}
+
+void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!rap)
+		return;
+
+	DBG("Detaching RAP from HCI, cleaning up mappings");
+
+	/* Cleanup the per-instance state machine */
+	if (sm) {
+		/* Unregister HCI events */
+		if (sm->event_id && sm->hci)
+			bt_hci_unregister(sm->hci, sm->event_id);
+
+		/* Clean up per-instance connection mappings */
+		remove_rap_mappings(sm);
+
+		/* Destroy the connection mappings queue */
+		queue_destroy(sm->conn_mappings, mapping_free);
+
+		/* Free the state machine */
+		free(sm);
+	}
+}
-- 


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* RE: Add initial Channel Sounding support for
  2026-04-21 11:31 ` [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
@ 2026-04-21 12:48   ` bluez.test.bot
  0 siblings, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2026-04-21 12:48 UTC (permalink / raw)
  To: linux-bluetooth, naga.akella

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

---Test result---

Test Summary:
CheckPatch                    PASS      2.60 seconds
GitLint                       FAIL      1.03 seconds
BuildEll                      PASS      20.01 seconds
BluezMake                     PASS      642.15 seconds
MakeCheck                     PASS      18.68 seconds
MakeDistcheck                 PASS      244.26 seconds
CheckValgrind                 PASS      291.29 seconds
CheckSmatch                   WARNING   346.14 seconds
bluezmakeextell               PASS      181.46 seconds
IncrementalBuild              PASS      687.56 seconds
ScanBuild                     PASS      996.61 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v11,1/3] shared: rap: Introduce Channel Sounding HCI raw interface support

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 (81>80): "[BlueZ,v11,1/3] shared: rap: Introduce Channel Sounding HCI raw interface support"
##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/main.c: note: in included file (through src/device.h):


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

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-04-21 12:48 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-21 11:31 [PATCH BlueZ v11 0/3] Add initial Channel Sounding support for Naga Bhavani Akella
2026-04-21 11:31 ` [PATCH BlueZ v11 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
2026-04-21 12:48   ` Add initial Channel Sounding support for bluez.test.bot
2026-04-21 11:31 ` [PATCH BlueZ v11 2/3] main.conf: Add Channel Sounding config parsing support Naga Bhavani Akella
2026-04-21 11:31 ` [PATCH BlueZ v11 3/3] profiles: ranging: Add HCI LE Event Handling in Reflector role Naga Bhavani Akella
  -- strict thread matches above, loose matches on Subject: below --
2026-04-17 13:56 [PATCH BlueZ v10 1/3] shared: rap: Introduce Channel Sounding HCI raw interface support Naga Bhavani Akella
2026-04-17 15:41 ` Add initial Channel Sounding support for bluez.test.bot

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