Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control
@ 2026-07-06 16:15 Naga Bhavani Akella
  2026-07-06 16:15 ` [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs Naga Bhavani Akella
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:15 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

This series adds user-facing control and configuration support for
Bluetooth LE Channel Sounding (CS) through a new D-Bus interface and
bluetoothctl integration.

The RAP profile is extended to expose an
org.bluez.ChannelSounding1 interface, allowing external applications
to configure default Channel Sounding settings and start or stop
measurement procedures. CS configuration, frequency, and default
settings are moved from hardcoded values into shared parameter
structures and accessor APIs, enabling runtime configuration from the
D-Bus layer.

To make the new functionality accessible from the command line,
bluetoothctl gains a dedicated "cs" submenu for configuring parameters,
starting and stopping measurements, and monitoring active sessions.

The series also ensures the Channel Sounding interface is available on
reflector devices by registering relevant GATT profiles even when GATT
client discovery is skipped, and updates the D-Bus documentation with
usage examples and bluetoothctl references.

Patch summary:
  1. rap: Add Channel Sounding parameter types and APIs
  2. profiles: Add D-Bus Channel Sounding control API
  3. src: Register GATT profiles for Channel Sounding reflector
  4. doc/org.bluez.ChannelSounding1: Add Used by reference and examples
  5. client: Add Channel Sounding shell submenu

This enables end-to-end Channel Sounding control through both D-Bus
clients and bluetoothctl while keeping measurement state synchronized
with controller procedure status


Naga Bhavani Akella (5):
  rap: Add Channel Sounding parameter types and APIs
  src: Register GATT profiles for Channel Sounding reflector
  doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  profiles: Add D-Bus Channel Sounding control API
  client: Add Channel Sounding shell submenu

 Makefile.am                        |   2 +-
 Makefile.tools                     |   3 +-
 client/cs.c                        | 981 +++++++++++++++++++++++++++++
 client/cs.h                        |  18 +
 client/main.c                      |  73 ++-
 doc/bluetoothctl-cs.rst            | 278 ++++++++
 doc/org.bluez.ChannelSounding1.rst | 302 +++++++++
 profiles/ranging/rap.c             | 405 +++++++++++-
 profiles/ranging/rap_hci.c         | 454 +++++++++----
 src/device.c                       |  28 +-
 src/shared/cs-types.h              |  55 ++
 src/shared/rap.h                   |  47 +-
 12 files changed, 2478 insertions(+), 168 deletions(-)
 create mode 100644 client/cs.c
 create mode 100644 client/cs.h
 create mode 100644 doc/bluetoothctl-cs.rst
 create mode 100644 doc/org.bluez.ChannelSounding1.rst
 create mode 100644 src/shared/cs-types.h

-- 


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

* [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs
  2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
@ 2026-07-06 16:15 ` Naga Bhavani Akella
  2026-07-06 17:05   ` Add D-Bus and bluetoothctl support for Channel Sounding control bluez.test.bot
  2026-07-06 16:16 ` [PATCH BlueZ v1 2/5] src: Register GATT profiles for Channel Sounding reflector Naga Bhavani Akella
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:15 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

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

Add getter/setter APIs for CS config, frequency
and default settings, add measurement.
---
 Makefile.am           |  2 +-
 src/shared/cs-types.h | 55 +++++++++++++++++++++++++++++++++++++++++++
 src/shared/rap.h      | 47 +++++++++++++++++++++++++++---------
 3 files changed, 92 insertions(+), 12 deletions(-)
 create mode 100644 src/shared/cs-types.h

diff --git a/Makefile.am b/Makefile.am
index 76c4ab5d4..a7b063c6e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -249,7 +249,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
 			src/shared/asha.h src/shared/asha.c \
 			src/shared/battery.h src/shared/battery.c \
 			src/shared/uinput.h src/shared/uinput.c \
-			src/shared/rap.h src/shared/rap.c
+			src/shared/rap.h src/shared/rap.c src/shared/cs-types.h
 
 
 if READLINE
diff --git a/src/shared/cs-types.h b/src/shared/cs-types.h
new file mode 100644
index 000000000..62ab643cf
--- /dev/null
+++ b/src/shared/cs-types.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#define BT_RAP_CS_MAX_CONFIGS   4
+#define BT_RAP_CS_MAX_FREQ_SETS 3
+
+struct bt_rap_le_cs_config {
+	uint8_t num_configs;
+	uint8_t config_id[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t main_mode_type[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t sub_mode_type[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t main_mode_min_steps[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t main_mode_max_steps[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t main_mode_repetition[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t mode0_steps[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t role[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t rtt_types[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t cs_sync_phy[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t channel_map[BT_RAP_CS_MAX_CONFIGS][10];
+	uint8_t channel_map_repetition[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t channel_selection_type[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t channel_shape[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t channel_jump[BT_RAP_CS_MAX_CONFIGS];
+	uint8_t companion_signal_enable[BT_RAP_CS_MAX_CONFIGS];
+};
+
+struct bt_rap_le_cs_frequency {
+	uint8_t  num_durations;
+	uint16_t max_procedure_duration[BT_RAP_CS_MAX_FREQ_SETS];
+	uint16_t min_period_between_procedures[BT_RAP_CS_MAX_FREQ_SETS];
+	uint16_t max_period_between_procedures[BT_RAP_CS_MAX_FREQ_SETS];
+	uint16_t max_procedure_count[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  min_sub_event_len[BT_RAP_CS_MAX_FREQ_SETS][3];
+	uint8_t  max_sub_event_len[BT_RAP_CS_MAX_FREQ_SETS][3];
+	uint8_t  tone_antenna_config_selection[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  phy[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  tx_power_delta[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  preferred_peer_antenna[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  snr_control_initiator[BT_RAP_CS_MAX_FREQ_SETS];
+	uint8_t  snr_control_reflector[BT_RAP_CS_MAX_FREQ_SETS];
+};
+
+struct bt_rap_le_cs_default_settings {
+	uint8_t role;
+	uint8_t cs_sync_ant_sel;
+	int8_t  max_tx_power;
+};
diff --git a/src/shared/rap.h b/src/shared/rap.h
index 7bc49f03d..22cb46653 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -9,8 +9,8 @@
 #include <inttypes.h>
 
 #include "src/shared/io.h"
-#include "bluetooth/mgmt.h"
 #include "src/shared/hci.h"
+#include "src/shared/cs-types.h"
 
 struct bt_rap;
 struct gatt_db;
@@ -189,22 +189,39 @@ bool bt_rap_unregister(unsigned int id);
 
 struct bt_rap *bt_rap_new(struct gatt_db *ldb, struct gatt_db *rdb);
 
+bool bt_rap_set_cs_config_params(void *hci_sm,
+				const struct bt_rap_le_cs_config *config);
+bool bt_rap_set_cs_freq_params(void *hci_sm,
+				const struct bt_rap_le_cs_frequency *frequency);
+bool bt_rap_set_default_settings_params(void *hci_sm,
+				const struct bt_rap_le_cs_default_settings *s);
+bool bt_rap_get_cs_config_params(void *hci_sm,
+				struct bt_rap_le_cs_config *config);
+bool bt_rap_get_cs_freq_params(void *hci_sm,
+				struct bt_rap_le_cs_frequency *frequency);
+bool bt_rap_get_default_settings_params(void *hci_sm,
+				struct bt_rap_le_cs_default_settings *s);
+/* Distance measurement control */
+bool bt_rap_start_measurement(void *hci_sm, uint16_t conn_handle,
+				uint32_t duration_secs);
+bool bt_rap_stop_measurement(void *hci_sm);
+
 /* HCI Raw Channel Approach */
 void bt_rap_hci_cs_config_complete_callback(uint16_t length,
-					     const void *param,
-					     void *user_data);
+				const void *param,
+				void *user_data);
 void bt_rap_hci_cs_sec_enable_complete_callback(uint16_t length,
-						 const void *param,
-						 void *user_data);
+				const void *param,
+				void *user_data);
 void bt_rap_hci_cs_procedure_enable_complete_callback(uint16_t length,
-						      const void *param,
-						      void *user_data);
+				const void *param,
+				void *user_data);
 void bt_rap_hci_cs_subevent_result_callback(uint16_t length,
-					     const void *param,
-					     void *user_data);
+				const void *param,
+				void *user_data);
 void bt_rap_hci_cs_subevent_result_cont_callback(uint16_t length,
-						  const void *param,
-						  void *user_data);
+				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,
@@ -220,3 +237,11 @@ bool bt_rap_set_conn_hndl(void *hci_sm,
 			bool is_central);
 
 void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
+
+bool bt_rap_is_procedure_active(void *hci_sm);
+
+void bt_rap_set_timeout_cb(void *hci_sm, void (*func)(void *),
+				void *user_data);
+
+void bt_rap_set_proc_active_cb(void *hci_sm, void (*func)(bool, void *),
+				void *user_data);
-- 


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

* [PATCH BlueZ v1 2/5] src: Register GATT profiles for Channel Sounding reflector
  2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
  2026-07-06 16:15 ` [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs Naga Bhavani Akella
@ 2026-07-06 16:16 ` Naga Bhavani Akella
  2026-07-06 16:16 ` [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples Naga Bhavani Akella
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:16 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

Probe and accept GATT_UUID profiles on non-initiator (reflector)
devices before gatt_client discovery checks,
ensuring RAP's org.bluez.ChannelSounding1 DBus interface is registered
even when GATT client discovery is skipped.
---
 src/device.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/device.c b/src/device.c
index 65d84be56..0227edb86 100644
--- a/src/device.c
+++ b/src/device.c
@@ -6301,17 +6301,31 @@ static void gatt_debug(const char *str, void *user_data)
 static void gatt_client_init(struct btd_device *device)
 {
 	uint8_t features;
+	GSList *uuid_list;
 
 	gatt_client_cleanup(device);
 
-	if (!btd_device_is_initiator(device) && !btd_opts.reverse_discovery) {
-		DBG("Reverse service discovery disabled: skipping GATT client");
-		return;
-	}
+	if (!btd_device_is_initiator(device)) {
+		/*
+		 * For the reflector role GATT client discovery is skipped, but
+		 * profiles that match GATT_UUID still need to be probed and
+		 * accepted so they can register their D-Bus interfaces (e.g.
+		 * org.bluez.ChannelSounding1).
+		 */
+		uuid_list = g_slist_append(NULL, (char *) GATT_UUID);
+		device_probe_profiles(device, uuid_list);
+		g_slist_free(uuid_list);
+		device_accept_gatt_profiles(device);
 
-	if (!btd_device_is_initiator(device) && !btd_opts.gatt_client) {
-		DBG("GATT client disabled: skipping GATT client");
-		return;
+		if (!btd_opts.reverse_discovery) {
+			DBG("Reverse srvc disc disabled: skip GATT client");
+			return;
+		}
+
+		if (!btd_opts.gatt_client) {
+			DBG("GATT client disabled: skipping GATT client");
+			return;
+		}
 	}
 
 	features =  BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING
-- 


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

* [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
  2026-07-06 16:15 ` [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs Naga Bhavani Akella
  2026-07-06 16:16 ` [PATCH BlueZ v1 2/5] src: Register GATT profiles for Channel Sounding reflector Naga Bhavani Akella
@ 2026-07-06 16:16 ` Naga Bhavani Akella
  2026-07-06 20:36   ` Luiz Augusto von Dentz
  2026-07-06 16:16 ` [PATCH BlueZ v1 4/5] profiles: Add D-Bus Channel Sounding control API Naga Bhavani Akella
  2026-07-06 16:16 ` [PATCH BlueZ v1 5/5] client: Add Channel Sounding shell submenu Naga Bhavani Akella
  4 siblings, 1 reply; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:16 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

Add :Used by: field linking to bluetoothctl cs submenu and
Examples section showing corresponding bluetoothctl cs commands
for D-Bus methods
---
 doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
 doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++
 2 files changed, 580 insertions(+)
 create mode 100644 doc/bluetoothctl-cs.rst
 create mode 100644 doc/org.bluez.ChannelSounding1.rst

diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
new file mode 100644
index 000000000..c16c89298
--- /dev/null
+++ b/doc/bluetoothctl-cs.rst
@@ -0,0 +1,278 @@
+================
+bluetoothctl-cs
+================
+
+--------------------------
+Channel Sounding Submenu
+--------------------------
+
+:Version: BlueZ
+:Copyright: Free use of this software is granted under the terms of the GNU
+            Lesser General Public Licenses (LGPL).
+:Date: June 2026
+:Manual section: 1
+:Manual group: Linux System Administration
+
+SYNOPSIS
+========
+
+**bluetoothctl** [--options] [cs.commands]
+
+This submenu controls Bluetooth Channel Sounding (CS) distance measurement
+using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
+starting and stopping measurements and inspecting the current parameter
+state and active session identifier.
+
+CS parameters can be overridden on any **start** call using inline
+``param=value`` arguments. Overrides are applied to the local parameter
+state before the measurement is started, so **show** reflects them
+immediately after.
+
+
+Channel Sounding Commands
+=========================
+
+start
+-----
+
+Sets one or more CS parameters and starts a distance measurement on the
+connected device. All configuration is sent to the daemon in a single
+**StartMeasurement** call. On success the device path is printed to
+the console. Multiple simultaneous sessions across different devices are
+supported; each is tracked independently.
+
+Calling **start** on a device that already has an active measurement
+returns an error without starting a second session on the same device.
+
+Positional arguments are optional and must appear before any
+``param=value`` pairs:
+
+- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
+  no timeout.
+
+Any additional argument of the form ``param=value`` overrides the named
+parameter for this call and for all subsequent calls. Array-valued
+parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
+use colon-separated hex bytes with no ``0x`` prefix.
+
+:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
+:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
+:[dev_addr]: Bluetooth address of the target device (optional; uses the
+             only available CS-capable device when omitted)
+:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
+:[param=value]: One or more ``param=value`` overrides (optional)
+
+**Settable parameters:**
+
+.. list-table::
+   :header-rows: 1
+   :widths: 35 15 50
+
+   * - Parameter
+     - Default
+     - Description
+   * - ``role``
+     - ``0x03``
+     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
+   * - ``cs_sync_ant_sel``
+     - ``0xFF``
+     - CS sync antenna selection (0xFE/0xFF reserved)
+   * - ``max_tx_power``
+     - ``20``
+     - Max TX power in dBm (signed, range −127 to +20)
+   * - ``config_id``
+     - ``0``
+     - CS configuration identifier
+   * - ``main_mode_type``
+     - ``1``
+     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
+   * - ``sub_mode_type``
+     - ``0xFF``
+     - Sub-mode within main mode; ``0xFF`` = unused
+   * - ``main_mode_min_steps``
+     - ``2``
+     - Min CS main mode steps per subevent
+   * - ``main_mode_max_steps``
+     - ``3``
+     - Max CS main mode steps per subevent
+   * - ``main_mode_repetition``
+     - ``1``
+     - Times main mode steps are repeated in a subevent
+   * - ``mode0_steps``
+     - ``2``
+     - CS Mode 0 steps at the beginning of each subevent
+   * - ``rtt_types``
+     - ``0``
+     - RTT measurement types bitmask
+   * - ``cs_sync_phy``
+     - ``0x01``
+     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
+   * - ``channel_map``
+     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
+     - 10-byte channel map bitmap (colon-separated hex)
+   * - ``channel_map_repetition``
+     - ``1``
+     - Consecutive repetitions of the channel map
+   * - ``channel_selection_type``
+     - ``0``
+     - CS channel selection algorithm
+   * - ``channel_shape``
+     - ``0``
+     - Shape used in channel selection algorithm
+   * - ``channel_jump``
+     - ``2``
+     - Channel jump size
+   * - ``companion_signal_enable``
+     - ``0``
+     - ``1`` to transmit companion signal, ``0`` to disable
+   * - ``max_procedure_duration``
+     - ``1600``
+     - Maximum duration of one CS measurement procedure
+   * - ``min_period_between_procedures``
+     - ``30``
+     - Minimum time between consecutive procedures
+   * - ``max_period_between_procedures``
+     - ``150``
+     - Maximum time between consecutive procedures
+   * - ``max_procedure_count``
+     - ``0``
+     - Max number of procedures; ``0`` = no limit
+   * - ``min_sub_event_len``
+     - ``00:20:00``
+     - Min CS subevent length, 3-byte LE (colon-separated hex)
+   * - ``max_sub_event_len``
+     - ``03:20:00``
+     - Max CS subevent length, 3-byte LE (colon-separated hex)
+   * - ``tone_antenna_config_selection``
+     - ``0x07``
+     - Antenna config for CS tone exchanges
+   * - ``phy``
+     - ``0x01``
+     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
+   * - ``tx_power_delta``
+     - ``0x80``
+     - Remote vs local TX power delta; ``0x80`` = not applicable
+   * - ``preferred_peer_antenna``
+     - ``0x03``
+     - Preferred antenna for the peer device
+   * - ``snr_control_initiator``
+     - ``0xFF``
+     - SNR control for initiator; ``0xFF`` = no preference
+   * - ``snr_control_reflector``
+     - ``0xFF``
+     - SNR control for reflector; ``0xFF`` = no preference
+
+:Example Start with all defaults, no timeout:
+	| **> start**
+:Example Start on a specific device:
+	| **> start AA:BB:CC:DD:EE:FF**
+:Example Start on a specific device with 10-second auto-stop:
+	| **> start AA:BB:CC:DD:EE:FF 10**
+:Example Start with 10-second auto-stop (single device, address omitted):
+	| **> start 0 10**
+:Example Start with no timeout, explicit:
+	| **> start**
+:Example Start with 5-minute auto-stop:
+	| **> start AA:BB:CC:DD:EE:FF 300**
+:Example Start as Initiator only:
+	| **> start role=0x01**
+:Example Start as Reflector only:
+	| **> start role=0x02**
+:Example Start as both Initiator and Reflector:
+	| **> start role=0x03**
+:Example Start with Mode 2 (PBR) main mode:
+	| **> start main_mode_type=2**
+:Example Start with both RTT and PBR modes:
+	| **> start main_mode_type=3**
+:Example Start with LE 2M PHY for CS procedures:
+	| **> start phy=0x02**
+:Example Start with LE 2M PHY for both CS sync and procedures:
+	| **> start cs_sync_phy=0x02 phy=0x02**
+:Example Start with reduced TX power (10 dBm):
+	| **> start max_tx_power=10**
+:Example Start with companion signal enabled:
+	| **> start companion_signal_enable=1**
+:Example Start with a procedure limit of 100:
+	| **> start max_procedure_count=100**
+:Example Start with high SNR preference on both roles:
+	| **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
+:Example Start with custom channel map (all enabled):
+	| **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
+:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
+	| **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
+:Example Start both roles, Mode 1, no limit, custom step counts:
+	| **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
+:Example Start on a device, Reflector, 60-second timeout:
+	| **> start AA:BB:CC:DD:EE:FF 60 role=0x02**
+
+defset
+------
+
+Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
+``max_tx_power``) on the connected device without starting a
+measurement. This is required for the Reflector role: a Reflector
+never calls **start** because it waits passively for the remote
+Initiator, so **defset** is the only way to push these settings to
+the daemon before the remote side initiates the procedure.
+
+Any ``param=value`` arguments update the local parameter state and
+are immediately sent to the daemon via **SetDefaultSettings**.
+Omitting all arguments sends the current local values unchanged.
+
+:Usage: **> defset [param=value ...]**
+:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
+:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
+               ``max_tx_power`` (optional)
+:Example Configure as Reflector:
+	| **> defset role=0x02**
+:Example Configure as Both with reduced TX power:
+	| **> defset role=0x03 max_tx_power=10**
+:Example Apply current local values without changing them:
+	| **> defset**
+
+stop
+----
+
+Stops an active CS distance measurement. When only one measurement is
+running the device address may be omitted. When multiple measurements
+are active the address is required to identify which one to stop.
+
+:Usage: **> stop [dev_addr]**
+:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
+:[dev_addr]: Bluetooth address of the device to stop (optional when
+             only one session is active; required otherwise)
+:Example Stop the only active measurement:
+	| **> stop**
+:Example Stop a specific device when multiple are active:
+	| **> stop AA:BB:CC:DD:EE:FF**
+:Example Stop a second device:
+	| **> stop 11:22:33:44:55:66**
+
+show
+----
+
+Displays all active measurements (device path for each) and the full
+set of CS parameter values that will be used on the next **start** call.
+When no measurements are active, ``none`` is shown.
+
+The parameter output is divided into three sections:
+
+- **Default Settings** — role, CS sync antenna selection, max TX power.
+- **CS Config Params** — per-procedure configuration fields including
+  mode type, step counts, PHY, and channel map.
+- **CS Frequency Params** — procedure scheduling fields including
+  duration, period, subevent lengths, and SNR control.
+
+:Usage: **> show**
+:Example Show active session and all CS parameters:
+	| **> show**
+
+RESOURCES
+=========
+
+http://www.bluez.org
+
+REPORTING BUGS
+==============
+
+linux-bluetooth@vger.kernel.org
diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
new file mode 100644
index 000000000..73f484507
--- /dev/null
+++ b/doc/org.bluez.ChannelSounding1.rst
@@ -0,0 +1,302 @@
+==========================
+org.bluez.ChannelSounding1
+==========================
+
+----------------------------------------------
+BlueZ D-Bus Channel Sounding API documentation
+----------------------------------------------
+
+:Version: BlueZ
+:Date: June 2026
+:Manual section: 5
+:Manual group: Linux System Administration
+
+Interface
+=========
+
+:Service:	org.bluez
+:Interface:	org.bluez.ChannelSounding1
+:Object path:	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+:Used by:	**bluetoothctl(1)**, **bluetoothctl-cs(1)**
+
+Methods
+-------
+
+void StartMeasurement(dict params)
+``````````````````````````````````
+
+Starts a Channel Sounding distance measurement procedure on the connected
+device. All configuration is supplied in a single ``a{sv}`` dictionary.
+Any key that is omitted retains its current value in the daemon.
+
+The device to measure is identified by the D-Bus object path on which
+this method is called
+(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
+Only one measurement per device object may be active at a time. Calling
+**StartMeasurement** while a session is already active returns
+``org.bluez.Error.InProgress``.
+
+Supported dictionary keys:
+
+:uint32 duration_secs (Default: 0):
+
+	Duration in seconds before the measurement is stopped
+	automatically. A value of 0 disables the automatic timeout.
+
+:byte role (Default: 0x03):
+
+	CS role to use for the measurement.
+
+	Possible values:
+
+	:0x01: Initiator
+	:0x02: Reflector
+	:0x03: Both (Initiator and Reflector)
+
+:byte cs_sync_ant_sel (Default: 0xFF):
+
+	CS sync antenna selection. Values 0xFE and 0xFF are reserved
+	by the Bluetooth specification.
+
+:byte max_tx_power (Default: 0x14):
+
+	Maximum TX power in dBm, treated as a signed value. Valid
+	range is -127 to +20 dBm.
+
+:byte config_id:
+
+	CS configuration identifier.
+
+:byte main_mode_type:
+
+	Main CS mode used in the procedure.
+
+:byte sub_mode_type:
+
+	Sub-mode within the main mode. Set to 0xFF when unused.
+
+:byte main_mode_min_steps:
+
+	Minimum number of CS main mode steps per CS subevent.
+
+:byte main_mode_max_steps:
+
+	Maximum number of CS main mode steps per CS subevent.
+
+:byte main_mode_repetition:
+
+	Number of times the main mode steps are repeated in a
+	subevent.
+
+:byte mode0_steps:
+
+	Number of CS Mode 0 steps at the beginning of each subevent.
+
+:byte rtt_types:
+
+	Round Trip Time measurement types for the configuration.
+
+:byte cs_sync_phy:
+
+	PHY used for CS sync packets.
+
+	Possible values:
+
+	:0x01: LE 1M PHY
+	:0x02: LE 2M PHY
+
+:array{byte} channel_map:
+
+	10-byte channel map bitmap. Must be exactly 10 bytes.
+
+:byte channel_map_repetition:
+
+	Number of consecutive repetitions of the channel map.
+
+:byte channel_selection_type:
+
+	Algorithm used for CS channel selection.
+
+:byte channel_shape:
+
+	Shape used in the channel selection algorithm.
+
+:byte channel_jump:
+
+	Channel jump size used in the channel selection algorithm.
+
+:byte companion_signal_enable:
+
+	Set to 1 to transmit a companion signal alongside the CS
+	tone, 0 to disable.
+
+:uint16 max_procedure_duration:
+
+	Maximum duration of a single CS measurement procedure.
+
+:uint16 min_period_between_procedures:
+
+	Minimum time between consecutive CS measurement procedures.
+
+:uint16 max_period_between_procedures:
+
+	Maximum time between consecutive CS measurement procedures.
+
+:uint16 max_procedure_count:
+
+	Maximum number of CS measurement procedures to run.
+	A value of 0 means no limit.
+
+:array{byte} min_sub_event_len:
+
+	Minimum CS subevent length as a 3-byte little-endian value.
+	Must be exactly 3 bytes.
+
+:array{byte} max_sub_event_len:
+
+	Maximum CS subevent length as a 3-byte little-endian value.
+	Must be exactly 3 bytes.
+
+:byte tone_antenna_config_selection:
+
+	Antenna configuration used for CS tone exchanges.
+
+:byte phy:
+
+	PHY used during CS procedures.
+
+	Possible values:
+
+	:0x01: LE 1M PHY
+	:0x02: LE 2M PHY
+
+:byte tx_power_delta:
+
+	Difference between remote and local TX power during CS
+	procedures. 0x80 indicates not applicable.
+
+:byte preferred_peer_antenna:
+
+	Preferred antenna to be used by the peer device.
+
+:byte snr_control_initiator:
+
+	SNR control setting for the initiator role.
+	0xFF indicates no preference.
+
+:byte snr_control_reflector:
+
+	SNR control setting for the reflector role.
+	0xFF indicates no preference.
+
+Possible errors:
+
+:org.bluez.Error.InProgress:
+:org.bluez.Error.InvalidArgs:
+:org.freedesktop.DBus.Error.Failed:
+
+Examples:
+
+:bluetoothctl set role then start:
+	| [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
+:bluetoothctl start with defaults:
+	| [cs] > start [dev_addr [duration_secs]]
+
+void SetDefaultSettings(dict params)
+`````````````````````````````````````
+
+Sets the CS default settings for this device without starting a
+measurement. This method is intended for the Reflector role, where
+the device waits passively for the remote Initiator to begin the
+procedure and therefore never calls **StartMeasurement**. It allows
+the application to configure ``role``, ``cs_sync_ant_sel``, and
+``max_tx_power`` ahead of time so they are in effect when the
+controller processes the remote CS configuration.
+Need this to set default settings in Reflector role.
+
+Supported dictionary keys:
+
+:byte role (Default: 0x03):
+
+	CS role to use for the measurement.
+
+	Possible values:
+
+	:0x01: Initiator
+	:0x02: Reflector
+	:0x03: Both (Initiator and Reflector)
+
+:byte cs_sync_ant_sel (Default: 0xFF):
+
+	CS sync antenna selection. Values 0xFE and 0xFF are reserved
+	by the Bluetooth specification.
+
+:byte max_tx_power (Default: 0x14):
+
+	Maximum TX power in dBm, treated as a signed value. Valid
+	range is -127 to +20 dBm.
+
+Possible errors:
+
+:org.bluez.Error.InvalidArgs:
+:org.freedesktop.DBus.Error.Failed:
+
+Examples:
+
+:bluetoothctl configure as Reflector:
+	| [cs] > defset role=0x02
+
+void StopMeasurement(void)
+``````````````````````````
+
+Stops the active Channel Sounding distance measurement on this device.
+The device is identified by the D-Bus object path on which this method
+is called — no session identifier is required.
+
+Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
+
+Possible errors:
+
+:org.bluez.Error.NotConnected:
+:org.freedesktop.DBus.Error.Failed:
+
+In **bluetoothctl(1)**, the device address argument may be omitted only
+when a single measurement is active; it is required when multiple
+measurements are active.
+
+Examples:
+
+:bluetoothctl stop the only active measurement:
+	| [cs] > stop
+:bluetoothctl stop a specific device when multiple are active:
+	| [cs] > stop AA:BB:CC:DD:EE:FF
+
+Properties
+----------
+
+boolean Active [readonly]
+`````````````````````````
+
+Indicates whether a CS distance measurement procedure is currently
+active on this device.
+
+Set to ``true`` when a procedure starts — either because the local
+Initiator called **StartMeasurement** successfully, or because the
+remote Initiator enabled a CS procedure on the local Reflector.
+
+Set to ``false`` when the procedure stops for any reason: the local
+application called **StopMeasurement**, the measurement duration timer
+expired, or the ACL connection was dropped.
+
+This property emits ``PropertiesChanged`` on every transition so that
+clients can track measurement state without polling.
+
+RESOURCES
+=========
+
+http://www.bluez.org
+
+REPORTING BUGS
+==============
+
+linux-bluetooth@vger.kernel.org
-- 


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

* [PATCH BlueZ v1 4/5] profiles: Add D-Bus Channel Sounding control API
  2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
                   ` (2 preceding siblings ...)
  2026-07-06 16:16 ` [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples Naga Bhavani Akella
@ 2026-07-06 16:16 ` Naga Bhavani Akella
  2026-07-06 16:16 ` [PATCH BlueZ v1 5/5] client: Add Channel Sounding shell submenu Naga Bhavani Akella
  4 siblings, 0 replies; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:16 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

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

Replace hardcoded CS configuration values with configurable HCI accessors
(bt_rap_{get,set}_cs_config_params, bt_rap_{get,set}_cs_freq_params,
bt_rap_{get,set}_default_settings_params).
Add measurement control APIs (bt_rap_start_measurement,
bt_rap_stop_measurement) with optional timeout-based auto-stop
and callbacks for timeout and procedure-state changes,
allowing DBus Active state to reflect actual controller procedure status.
---
 profiles/ranging/rap.c     | 405 ++++++++++++++++++++++++++++++---
 profiles/ranging/rap_hci.c | 454 +++++++++++++++++++++++++++----------
 2 files changed, 712 insertions(+), 147 deletions(-)

diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
index fa3e27328..deb03b139 100644
--- a/profiles/ranging/rap.c
+++ b/profiles/ranging/rap.c
@@ -35,7 +35,11 @@
 #include "src/shared/rap.h"
 #include "attrib/att.h"
 #include "src/log.h"
+#include "src/shared/cs-types.h"
 #include "src/btd.h"
+#include "src/dbus-common.h"
+
+#define CS_INTERFACE "org.bluez.ChannelSounding1"
 
 struct rap_adapter_data {
 	struct btd_adapter *adapter;
@@ -43,6 +47,14 @@ struct rap_adapter_data {
 	int ref_count;  /* Number of devices using this adapter */
 };
 
+struct cs_session {
+	bool active;
+	uint32_t duration_secs;
+	struct bt_rap_le_cs_default_settings settings;
+	struct bt_rap_le_cs_config    cfg;
+	struct bt_rap_le_cs_frequency freq;
+};
+
 struct rap_data {
 	struct btd_device *device;
 	struct btd_service *service;
@@ -50,6 +62,8 @@ struct rap_data {
 	unsigned int ready_id;
 	struct rap_adapter_data *adapter_data;  /* Shared adapter-level HCI */
 	void *hci_sm;  /* Per-device HCI state machine */
+	uint16_t conn_handle;  /* Last known connection handle */
+	struct cs_session active_session;  /* active==false when idle */
 };
 
 static struct queue *sessions;
@@ -293,6 +307,335 @@ static void rap_attached(struct bt_rap *rap, void *user_data)
 	rap_data_add(data);
 }
 
+static DBusMessage *set_default_settings(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct rap_data *data = user_data;
+	struct bt_rap_le_cs_default_settings settings;
+	DBusMessageIter iter, dict;
+
+	bt_rap_get_default_settings_params(data->hci_sm, &settings);
+
+	dbus_message_iter_init(msg, &iter);
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Expected a{sv} dictionary");
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, variant;
+		const char *key;
+		int vtype;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &variant);
+		vtype = dbus_message_iter_get_arg_type(&variant);
+
+		if (strcmp(key, "role") == 0) {
+			if (vtype != DBUS_TYPE_BYTE)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant, &settings.role);
+		} else if (strcmp(key, "cs_sync_ant_sel") == 0) {
+			if (vtype != DBUS_TYPE_BYTE)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant,
+						    &settings.cs_sync_ant_sel);
+		} else if (strcmp(key, "max_tx_power") == 0) {
+			uint8_t bval;
+
+			if (vtype != DBUS_TYPE_BYTE)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant, &bval);
+			settings.max_tx_power = (int8_t)bval;
+		}
+
+		dbus_message_iter_next(&dict);
+		continue;
+bad_type:
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Unexpected variant type for key");
+	}
+
+	if (!bt_rap_set_default_settings_params(data->hci_sm, &settings))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					"Set default settings failed");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *start_measurement(DBusConnection *conn,
+				DBusMessage *msg, void *user_data)
+{
+	struct rap_data *data = user_data;
+	struct bt_rap_le_cs_config cfg;
+	struct bt_rap_le_cs_frequency freq;
+	struct bt_rap_le_cs_default_settings settings;
+	uint32_t duration_secs = 0;
+	DBusMessageIter iter, dict;
+
+	if (data->active_session.active)
+		return g_dbus_create_error(msg,
+					"org.bluez.Error.InProgress",
+					"Measurement already active");
+
+	/* Seed locals from current state-machine defaults */
+	bt_rap_get_cs_config_params(data->hci_sm, &cfg);
+	bt_rap_get_cs_freq_params(data->hci_sm, &freq);
+	bt_rap_get_default_settings_params(data->hci_sm, &settings);
+
+	dbus_message_iter_init(msg, &iter);
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					   "Expected a{sv} dictionary");
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, variant;
+		const char *key;
+		int vtype;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &variant);
+		vtype = dbus_message_iter_get_arg_type(&variant);
+
+		if (strcmp(key, "duration_secs") == 0) {
+			if (vtype != DBUS_TYPE_UINT32)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant, &duration_secs);
+		} else if (strcmp(key, "role") == 0 &&
+			   vtype == DBUS_TYPE_BYTE) {
+			dbus_message_iter_get_basic(&variant, &settings.role);
+		} else if (strcmp(key, "cs_sync_ant_sel") == 0 &&
+			   vtype == DBUS_TYPE_BYTE) {
+			dbus_message_iter_get_basic(&variant,
+						    &settings.cs_sync_ant_sel);
+		} else if (strcmp(key, "max_tx_power") == 0 &&
+			   vtype == DBUS_TYPE_BYTE) {
+			uint8_t bval;
+
+			dbus_message_iter_get_basic(&variant, &bval);
+			settings.max_tx_power = (int8_t)bval;
+		} else if (strcmp(key, "channel_map") == 0) {
+			DBusMessageIter array;
+			uint8_t *bytes;
+			int len;
+
+			if (vtype != DBUS_TYPE_ARRAY)
+				goto bad_type;
+			dbus_message_iter_recurse(&variant, &array);
+			dbus_message_iter_get_fixed_array(&array, &bytes, &len);
+			if (len != 10)
+				return g_dbus_create_error(msg,
+						DBUS_ERROR_INVALID_ARGS,
+						"channel_map must be 10 bytes");
+			memcpy(cfg.channel_map[0], bytes, 10);
+		} else if (strcmp(key, "min_sub_event_len") == 0 ||
+			   strcmp(key, "max_sub_event_len") == 0) {
+			DBusMessageIter array;
+			uint8_t *bytes;
+			int len;
+
+			if (vtype != DBUS_TYPE_ARRAY)
+				goto bad_type;
+			dbus_message_iter_recurse(&variant, &array);
+			dbus_message_iter_get_fixed_array(&array, &bytes, &len);
+			if (len != 3)
+				return g_dbus_create_error(msg,
+						DBUS_ERROR_INVALID_ARGS,
+						"sub_event_len must be 3 bytes");
+			if (strcmp(key, "min_sub_event_len") == 0)
+				memcpy(freq.min_sub_event_len[0], bytes, 3);
+			else
+				memcpy(freq.max_sub_event_len[0], bytes, 3);
+		} else if (strcmp(key, "max_procedure_duration") == 0 ||
+			   strcmp(key, "min_period_between_procedures") == 0 ||
+			   strcmp(key, "max_period_between_procedures") == 0 ||
+			   strcmp(key, "max_procedure_count") == 0) {
+			uint16_t u16val;
+
+			if (vtype != DBUS_TYPE_UINT16)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant, &u16val);
+
+			if (strcmp(key, "max_procedure_duration") == 0)
+				freq.max_procedure_duration[0] = u16val;
+			else if (strcmp(key,
+					"min_period_between_procedures") == 0)
+				freq.min_period_between_procedures[0] = u16val;
+			else if (strcmp(key,
+					"max_period_between_procedures") == 0)
+				freq.max_period_between_procedures[0] = u16val;
+			else
+				freq.max_procedure_count[0] = u16val;
+		} else {
+			/* All remaining keys expect a byte variant */
+			uint8_t bval;
+
+			if (vtype != DBUS_TYPE_BYTE)
+				goto bad_type;
+			dbus_message_iter_get_basic(&variant, &bval);
+
+			if (strcmp(key, "config_id") == 0)
+				cfg.config_id[0] = bval;
+			else if (strcmp(key, "main_mode_type") == 0)
+				cfg.main_mode_type[0] = bval;
+			else if (strcmp(key, "sub_mode_type") == 0)
+				cfg.sub_mode_type[0] = bval;
+			else if (strcmp(key, "main_mode_min_steps") == 0)
+				cfg.main_mode_min_steps[0] = bval;
+			else if (strcmp(key, "main_mode_max_steps") == 0)
+				cfg.main_mode_max_steps[0] = bval;
+			else if (strcmp(key, "main_mode_repetition") == 0)
+				cfg.main_mode_repetition[0] = bval;
+			else if (strcmp(key, "mode0_steps") == 0)
+				cfg.mode0_steps[0] = bval;
+			else if (strcmp(key, "rtt_types") == 0)
+				cfg.rtt_types[0] = bval;
+			else if (strcmp(key, "cs_sync_phy") == 0)
+				cfg.cs_sync_phy[0] = bval;
+			else if (strcmp(key, "channel_map_repetition") == 0)
+				cfg.channel_map_repetition[0] = bval;
+			else if (strcmp(key, "channel_selection_type") == 0)
+				cfg.channel_selection_type[0] = bval;
+			else if (strcmp(key, "channel_shape") == 0)
+				cfg.channel_shape[0] = bval;
+			else if (strcmp(key, "channel_jump") == 0)
+				cfg.channel_jump[0] = bval;
+			else if (strcmp(key, "companion_signal_enable") == 0)
+				cfg.companion_signal_enable[0] = bval;
+			else if (strcmp(key,
+					"tone_antenna_config_selection") == 0)
+				freq.tone_antenna_config_selection[0] = bval;
+			else if (strcmp(key, "phy") == 0)
+				freq.phy[0] = bval;
+			else if (strcmp(key, "tx_power_delta") == 0)
+				freq.tx_power_delta[0] = bval;
+			else if (strcmp(key, "preferred_peer_antenna") == 0)
+				freq.preferred_peer_antenna[0] = bval;
+			else if (strcmp(key, "snr_control_initiator") == 0)
+				freq.snr_control_initiator[0] = bval;
+			else if (strcmp(key, "snr_control_reflector") == 0)
+				freq.snr_control_reflector[0] = bval;
+		}
+
+		dbus_message_iter_next(&dict);
+		continue;
+bad_type:
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					   "Unexpected variant type for key");
+	}
+
+	if (data->conn_handle == 0)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					   "Device not connected");
+
+	if (!bt_rap_set_default_settings_params(data->hci_sm, &settings))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					   "Set default settings failed");
+
+	if (!bt_rap_set_cs_config_params(data->hci_sm, &cfg))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					   "Set CS config params failed");
+
+	if (!bt_rap_set_cs_freq_params(data->hci_sm, &freq))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					   "Set CS freq params failed");
+
+	if (!bt_rap_start_measurement(data->hci_sm, data->conn_handle,
+							duration_secs))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					   "Start measurement failed");
+
+	data->active_session.active        = true;
+	data->active_session.duration_secs = duration_secs;
+	data->active_session.settings      = settings;
+	data->active_session.cfg           = cfg;
+	data->active_session.freq          = freq;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *stop_measurement(DBusConnection *conn,
+				DBusMessage *msg, void *user_data)
+{
+	struct rap_data *data = user_data;
+
+	if (!data->active_session.active)
+		return g_dbus_create_error(msg,
+					"org.bluez.Error.NotConnected",
+					"No active measurement");
+
+	if (!bt_rap_stop_measurement(data->hci_sm))
+		return g_dbus_create_error(msg, DBUS_ERROR_FAILED,
+					"Stop measurement failed");
+
+	memset(&data->active_session, 0, sizeof(data->active_session));
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				device_get_path(data->device),
+				CS_INTERFACE, "Active");
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cs_dbus_methods[] = {
+	{ GDBUS_METHOD("SetDefaultSettings",
+			GDBUS_ARGS({ "params", "a{sv}" }),
+			NULL,
+			set_default_settings) },
+	{ GDBUS_METHOD("StartMeasurement",
+			GDBUS_ARGS({ "params", "a{sv}" }),
+			NULL,
+			start_measurement) },
+	{ GDBUS_METHOD("StopMeasurement",
+			NULL,
+			NULL,
+			stop_measurement) },
+	{ }
+};
+
+static gboolean cs_property_get_active(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct rap_data *data = user_data;
+	dbus_bool_t active = data->active_session.active ||
+				bt_rap_is_procedure_active(data->hci_sm);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &active);
+	return TRUE;
+}
+
+static const GDBusPropertyTable cs_dbus_properties[] = {
+	{ "Active", "b", cs_property_get_active, NULL, NULL, 0 },
+	{ }
+};
+
+static void rap_measurement_timeout_cb(void *user_data)
+{
+	struct rap_data *data = user_data;
+
+	memset(&data->active_session, 0, sizeof(data->active_session));
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				device_get_path(data->device),
+				CS_INTERFACE, "Active");
+}
+
+static void rap_proc_active_changed(bool active, void *user_data)
+{
+	struct rap_data *data = user_data;
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+				device_get_path(data->device),
+				CS_INTERFACE, "Active");
+}
+
 static int rap_probe(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
@@ -329,6 +672,10 @@ static int rap_probe(struct btd_service *service)
 
 	bt_rap_set_user_data(data->rap, service);
 
+	bt_rap_set_timeout_cb(data->hci_sm, rap_measurement_timeout_cb, data);
+
+	bt_rap_set_proc_active_cb(data->hci_sm, rap_proc_active_changed, data);
+
 	return 0;
 }
 
@@ -347,13 +694,16 @@ static void rap_remove(struct btd_service *service)
 		return;
 	}
 
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+				    device_get_path(device),
+				    CS_INTERFACE);
+
 	rap_data_remove(data);
 }
 
 static int rap_accept(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
-	struct btd_adapter *adapter = device_get_adapter(device);
 	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;
@@ -370,33 +720,6 @@ static int rap_accept(struct btd_service *service)
 		return -EINVAL;
 	}
 
-	/* init shared adapter HCI channel */
-	if (!data->adapter_data) {
-		data->adapter_data = rap_adapter_data_ref(adapter);
-		if (!data->adapter_data) {
-			error("Failed to get adapter HCI channel");
-			return -EINVAL;
-		}
-		DBG("Using shared HCI channel for adapter (ref_count=%d)",
-			data->adapter_data->ref_count);
-	}
-
-	/* per-device HCI state machine */
-	if (!data->hci_sm) {
-		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);
-			data->adapter_data = NULL;
-			return -EINVAL;
-		}
-		DBG("HCI state machine attached successfully for device");
-	}
-
 	if (!bt_rap_attach(data->rap, client)) {
 		error("RAP unable to attach");
 		return -EINVAL;
@@ -411,8 +734,7 @@ static int rap_accept(struct btd_service *service)
 		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);
+			data->conn_handle = handle;
 			bt_rap_set_conn_hndl(data->hci_sm,
 					data->rap, handle,
 					(const uint8_t *) bdaddr,
@@ -426,12 +748,33 @@ static int rap_accept(struct btd_service *service)
 
 	btd_service_connecting_complete(service, 0);
 
+	g_dbus_register_interface(btd_get_dbus_connection(),
+				  device_get_path(data->device),
+				  CS_INTERFACE, cs_dbus_methods,
+				  NULL, cs_dbus_properties, data, NULL);
+
 	return 0;
 }
 
 static int rap_disconnect(struct btd_service *service)
 {
-	DBG(" ");
+	struct rap_data *data = btd_service_get_user_data(service);
+	char addr[18];
+
+	ba2str(device_get_address(btd_service_get_device(service)), addr);
+	DBG("%s", addr);
+	if (!data) {
+		error("RAP Service not handled by profile");
+		return -EINVAL;
+	}
+
+	if (data && data->hci_sm && data->conn_handle) {
+		bt_rap_clear_conn_handle(data->hci_sm, data->conn_handle);
+		data->conn_handle = 0;
+	}
+
+	memset(&data->active_session, 0, sizeof(data->active_session));
+
 	btd_service_disconnecting_complete(service, 0);
 	return 0;
 }
diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
index 13f1365f3..daae66359 100644
--- a/profiles/ranging/rap_hci.c
+++ b/profiles/ranging/rap_hci.c
@@ -21,6 +21,8 @@
 #include "lib/bluetooth/bluetooth.h"
 #include "src/shared/util.h"
 #include "src/shared/queue.h"
+#include "src/shared/timeout.h"
+#include "src/shared/cs-types.h"
 #include "src/shared/rap.h"
 #include "src/shared/att.h"
 #include "src/log.h"
@@ -60,19 +62,28 @@ _Static_assert(ARRAY_SIZE(state_names) == CS_STATE_UNSPECIFIED + 1,
 typedef void (*cs_callback_t)(uint16_t length,
 			const void *param, void *user_data);
 
-/* State Machine Context */
+typedef void (*cs_timeout_func_t)(void *user_data);
+
 struct cs_state_machine {
 	enum cs_state current_state;
 	enum cs_state old_state;
 	struct bt_hci *hci;
 	struct bt_rap *rap;
 	struct queue *event_ids;
+	unsigned int measurement_timeout_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 */
+	uint16_t active_conn_handle;  /* current measurement handle */
 	struct timespec last_chan_class_time;  /* For 1-second rate limit */
+	struct bt_rap_le_cs_config   cs_config;
+	struct bt_rap_le_cs_frequency cs_frequency;
+	cs_timeout_func_t timeout_func;
+	void *timeout_data;
+	void (*proc_active_func)(bool active, void *user_data);
+	void *proc_active_data;
 };
 
 /* Connection Handle Mapping */
@@ -85,7 +96,83 @@ struct rap_conn_mapping {
 	struct bt_rap *rap;
 };
 
-/* Connection Mapping Helper Functions */
+bool bt_rap_set_cs_config_params(void *hci_sm,
+				 const struct bt_rap_le_cs_config *config)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !config)
+		return false;
+
+	sm->cs_config = *config;
+	return true;
+}
+
+bool bt_rap_set_cs_freq_params(void *hci_sm,
+			       const struct bt_rap_le_cs_frequency *frequency)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !frequency)
+		return false;
+
+	sm->cs_frequency = *frequency;
+	return true;
+}
+
+bool bt_rap_set_default_settings_params(void *hci_sm,
+			const struct bt_rap_le_cs_default_settings *settings)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !settings)
+		return false;
+
+	sm->role_enable            = settings->role;
+	sm->cs_opt.role            = settings->role;
+	sm->cs_opt.cs_sync_ant_sel = settings->cs_sync_ant_sel;
+	sm->cs_opt.max_tx_power    = settings->max_tx_power;
+	return true;
+}
+
+bool bt_rap_get_cs_config_params(void *hci_sm,
+				 struct bt_rap_le_cs_config *config)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !config)
+		return false;
+
+	*config = sm->cs_config;
+	return true;
+}
+
+bool bt_rap_get_cs_freq_params(void *hci_sm,
+			       struct bt_rap_le_cs_frequency *frequency)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !frequency)
+		return false;
+
+	*frequency = sm->cs_frequency;
+	return true;
+}
+
+bool bt_rap_get_default_settings_params(void *hci_sm,
+			struct bt_rap_le_cs_default_settings *settings)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !settings)
+		return false;
+
+	settings->role            = sm->cs_opt.role;
+	settings->cs_sync_ant_sel = sm->cs_opt.cs_sync_ant_sel;
+	settings->max_tx_power    = sm->cs_opt.max_tx_power;
+	return true;
+}
+
 static void mapping_free(void *data)
 {
 	struct rap_conn_mapping *mapping = data;
@@ -173,6 +260,12 @@ static void cs_state_machine_init(struct cs_state_machine *sm,
 				uint8_t role, uint8_t cs_sync_ant_sel,
 				int8_t max_tx_power)
 {
+	static const uint8_t ch_map[10] = {
+		0xFC, 0xFF, 0x7F, 0xFC, 0xFF,
+		0xFF, 0xFF, 0xFF, 0xFF, 0x1F
+	};
+	uint8_t i;
+
 	if (!sm)
 		return;
 
@@ -185,15 +278,50 @@ static void cs_state_machine_init(struct cs_state_machine *sm,
 
 	/* 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.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 */
+	sm->cs_opt.max_tx_power    = max_tx_power;
+	sm->cs_opt.rtt_type        = 0;
+
+	sm->cs_config.num_configs = BT_RAP_CS_MAX_CONFIGS;
+	for (i = 0; i < BT_RAP_CS_MAX_CONFIGS; i++) {
+		sm->cs_config.config_id[i]              = i;
+		sm->cs_config.main_mode_type[i]         = 0x01;
+		sm->cs_config.sub_mode_type[i]          = 0xFF;
+		sm->cs_config.main_mode_min_steps[i]    = 0x02;
+		sm->cs_config.main_mode_max_steps[i]    = 0x03;
+		sm->cs_config.main_mode_repetition[i]   = 0x01;
+		sm->cs_config.mode0_steps[i]            = 0x02;
+		sm->cs_config.role[i]                   = 0x00;
+		sm->cs_config.rtt_types[i]              = 0x00;
+		sm->cs_config.cs_sync_phy[i]            = 0x01;
+		memcpy(sm->cs_config.channel_map[i], ch_map, 10);
+		sm->cs_config.channel_map_repetition[i] = 0x01;
+		sm->cs_config.channel_selection_type[i] = 0x00;
+		sm->cs_config.channel_shape[i]          = 0x00;
+		sm->cs_config.channel_jump[i]           = 0x02;
+		sm->cs_config.companion_signal_enable[i] = 0x00;
+	}
+
+	sm->cs_frequency.num_durations = BT_RAP_CS_MAX_FREQ_SETS;
+	for (i = 0; i < BT_RAP_CS_MAX_FREQ_SETS; i++) {
+		sm->cs_frequency.max_procedure_duration[i]        = 0x0640;
+		sm->cs_frequency.min_period_between_procedures[i] = 0x001E;
+		sm->cs_frequency.max_period_between_procedures[i] = 0x0096;
+		sm->cs_frequency.max_procedure_count[i]           = 0x0000;
+		sm->cs_frequency.min_sub_event_len[i][0]          = 0x00;
+		sm->cs_frequency.min_sub_event_len[i][1]          = 0x20;
+		sm->cs_frequency.min_sub_event_len[i][2]          = 0x00;
+		sm->cs_frequency.max_sub_event_len[i][0]          = 0x03;
+		sm->cs_frequency.max_sub_event_len[i][1]          = 0x20;
+		sm->cs_frequency.max_sub_event_len[i][2]          = 0x00;
+		sm->cs_frequency.tone_antenna_config_selection[i] = 0x07;
+		sm->cs_frequency.phy[i]                           = 0x01;
+		sm->cs_frequency.tx_power_delta[i]                = 0x80;
+		sm->cs_frequency.preferred_peer_antenna[i]        = 0x03;
+		sm->cs_frequency.snr_control_initiator[i]         = 0xFF;
+		sm->cs_frequency.snr_control_reflector[i]         = 0xFF;
+	}
 }
 
 /* State Transition Logic */
@@ -228,10 +356,17 @@ static enum cs_state cs_get_current_state(struct cs_state_machine *sm)
 
 static bool is_initiator_role(const struct cs_state_machine *sm)
 {
-	return sm->role_enable == 0x01 || sm->role_enable == 0x03;
+	return sm->role_enable == 0x01;
+}
+
+/* For role_enable=0x03 (Both), Central acts as Initiator and Peripheral as
+ * Reflector — the link layer topology determines the CS role assignment.
+ */
+static bool is_both_role(const struct cs_state_machine *sm)
+{
+	return sm->role_enable == 0x03;
 }
 
-/* Helper function to send read remote capabilities for all connections */
 static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
 		uint16_t handle)
 {
@@ -262,20 +397,6 @@ static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
 	return true;
 }
 
-/* Helper function to send read remote capabilities for all connections */
-static void send_read_remote_cap_for_mapping(void *data, void *user_data)
-{
-	struct rap_conn_mapping *mapping = data;
-	struct cs_state_machine *sm = user_data;
-
-	if (!mapping || !sm)
-		return;
-
-	DBG("Sending read remote capabilities for handle 0x%04X",
-		mapping->handle);
-	bt_rap_read_remote_supported_capabilities(sm, mapping->handle);
-}
-
 /* HCI Event Callbacks */
 static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
 					void *user_data)
@@ -324,12 +445,9 @@ static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
 	/* Transition to INIT state before reading remote capabilities */
 	cs_set_state(sm, CS_STATE_INIT);
 
-	/* Send read remote capabilities for all connected devices */
-	if (sm->conn_mappings) {
-		DBG("Sending read remote capabilities for all connections");
-		queue_foreach(sm->conn_mappings,
-				send_read_remote_cap_for_mapping, sm);
-	}
+	DBG("Sending read remote capabilities for handle 0x%04X",
+		sm->active_conn_handle);
+	bt_rap_read_remote_supported_capabilities(sm, sm->active_conn_handle);
 }
 
 static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
@@ -338,11 +456,6 @@ static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
 	struct bt_hci_cmd_le_cs_create_config cmd;
 	unsigned int status;
 
-	uint8_t channel_map[10] = {
-		0xFC, 0xFF, 0x7F, 0xFC, 0xFF,
-		0xFF, 0xFF, 0xFF, 0xFF, 0x1F
-	};
-
 	if (!sm || !sm->hci) {
 		error("CS Create Config: sm or hci is null");
 		return;
@@ -353,22 +466,22 @@ static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.handle = cpu_to_le16(handle);
 	cmd.create_context = 1;
-	/* Default values, will change to pick user given values later */
-	cmd.config_id              = 0x00;
-	cmd.main_mode_type         = 0x01;
-	cmd.sub_mode_type          = 0xFF;
-	cmd.min_main_mode_steps    = 0x02;
-	cmd.max_main_mode_steps    = 0x03;
-	cmd.main_mode_repetition   = 0x01;
-	cmd.mode_0_steps           = 0x02;
-	cmd.role                   = 0x00;
-	cmd.rtt_type               = 0x00;
-	cmd.cs_sync_phy            = 0x01;
-	memcpy(cmd.channel_map, channel_map, 10);
-	cmd.channel_map_repetition = 0x01;
-	cmd.channel_selection_type = 0x00;
-	cmd.ch3c_shape             = 0x00;
-	cmd.ch3c_jump              = 0x02;
+
+	cmd.config_id              = sm->cs_config.config_id[0];
+	cmd.main_mode_type         = sm->cs_config.main_mode_type[0];
+	cmd.sub_mode_type          = sm->cs_config.sub_mode_type[0];
+	cmd.min_main_mode_steps    = sm->cs_config.main_mode_min_steps[0];
+	cmd.max_main_mode_steps    = sm->cs_config.main_mode_max_steps[0];
+	cmd.main_mode_repetition   = sm->cs_config.main_mode_repetition[0];
+	cmd.mode_0_steps           = sm->cs_config.mode0_steps[0];
+	cmd.role                   = sm->cs_config.role[0];
+	cmd.rtt_type               = sm->cs_config.rtt_types[0];
+	cmd.cs_sync_phy            = sm->cs_config.cs_sync_phy[0];
+	memcpy(cmd.channel_map, sm->cs_config.channel_map[0], 10);
+	cmd.channel_map_repetition = sm->cs_config.channel_map_repetition[0];
+	cmd.channel_selection_type = sm->cs_config.channel_selection_type[0];
+	cmd.ch3c_shape             = sm->cs_config.channel_shape[0];
+	cmd.ch3c_jump              = sm->cs_config.channel_jump[0];
 	cmd.reserved               = 0x00;
 
 	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_CREATE_CONFIG,
@@ -388,6 +501,7 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
 {
 	const struct bt_hci_rsp_le_cs_set_def_settings *rp;
 	struct cs_state_machine *sm = user_data;
+	struct rap_conn_mapping *m;
 
 	if (!sm || !data || size < sizeof(*rp))
 		return;
@@ -403,14 +517,24 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
 	}
 
 	if (rp->status == 0) {
-		/* Success - proceed to configuration */
 		cs_set_state(sm, CS_STATE_WAIT_CONFIG_CMPLT);
 
-		/* If role is initiator, send CS Create Config command */
-		if (is_initiator_role(sm))
+		/* Send CS Create Config if we are (or will act as) Initiator.
+		 * For role_enable=Both, Central acts as Initiator.
+		 */
+		if (is_initiator_role(sm)) {
 			rap_send_hci_cs_create_config_command(sm, rp->handle);
-		else
+		} else if (is_both_role(sm)) {
+			m = find_mapping_by_handle(sm, rp->handle);
+
+			if (m && m->is_central)
+				rap_send_hci_cs_create_config_command(sm,
+								rp->handle);
+			else
+				DBG("Both role: Wait for CS Config Cmpl");
+		} else {
 			DBG("Reflector role: Waiting for CS Config Completed");
+		}
 	} else {
 		/* Error - transition to stopped */
 		error("CS Set default setting failed with status 0x%02X",
@@ -434,7 +558,7 @@ static void rap_send_hci_cs_remove_config_command(struct cs_state_machine *sm,
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.handle = cpu_to_le16(handle);
-	cmd.config_id = 0x00;  /* Default config ID */
+	cmd.config_id = sm->cs_config.config_id[0];
 
 	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_REMOVE_CONFIG,
 				&cmd, sizeof(cmd), NULL, sm, NULL);
@@ -481,13 +605,6 @@ static bool rap_send_hci_cs_set_procedure_parameters(
 {
 	struct bt_hci_cmd_le_cs_set_proc_params cmd;
 	unsigned int status;
-	uint8_t min_sub_event_len[3] = {
-		0x00, 0x20, 0x00
-	};
-
-	uint8_t max_sub_event_len[3] = {
-		0x03, 0x20, 0x00
-	};
 
 	if (!sm || !sm->hci) {
 		error("CS Set Procedure Parameters: sm or hci is null");
@@ -498,20 +615,25 @@ static bool rap_send_hci_cs_set_procedure_parameters(
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.handle = cpu_to_le16(handle);
-	/* Default values, will change to pick user given values later */
-	cmd.config_id = 0x00;
-	cmd.max_procedure_len = 0x0640;
-	cmd.min_procedure_interval = 0x1E;
-	cmd.max_procedure_interval = 0x96;
-	cmd.max_procedure_count = 0x00;
-	memcpy(cmd.min_subevent_len, min_sub_event_len, 3);
-	memcpy(cmd.max_subevent_len, max_sub_event_len, 3);
-	cmd.tone_antenna_config_selection = 0x07;
-	cmd.phy                    = 0x01;
-	cmd.tx_power_delta         = 0x80;
-	cmd.preferred_peer_antenna = 0x03;
-	cmd.snr_control_initiator  = 0xFF;
-	cmd.snr_control_reflector  = 0xFF;
+
+	cmd.config_id = sm->cs_config.config_id[0];
+	cmd.max_procedure_len =
+		cpu_to_le16(sm->cs_frequency.max_procedure_duration[0]);
+	cmd.min_procedure_interval =
+		cpu_to_le16(sm->cs_frequency.min_period_between_procedures[0]);
+	cmd.max_procedure_interval =
+		cpu_to_le16(sm->cs_frequency.max_period_between_procedures[0]);
+	cmd.max_procedure_count =
+		cpu_to_le16(sm->cs_frequency.max_procedure_count[0]);
+	memcpy(cmd.min_subevent_len, sm->cs_frequency.min_sub_event_len[0], 3);
+	memcpy(cmd.max_subevent_len, sm->cs_frequency.max_sub_event_len[0], 3);
+	cmd.tone_antenna_config_selection =
+		sm->cs_frequency.tone_antenna_config_selection[0];
+	cmd.phy                    = sm->cs_frequency.phy[0];
+	cmd.tx_power_delta         = sm->cs_frequency.tx_power_delta[0];
+	cmd.preferred_peer_antenna = sm->cs_frequency.preferred_peer_antenna[0];
+	cmd.snr_control_initiator  = sm->cs_frequency.snr_control_initiator[0];
+	cmd.snr_control_reflector  = sm->cs_frequency.snr_control_reflector[0];
 
 	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_PROC_PARAMS,
 				&cmd, sizeof(cmd), NULL, sm, NULL);
@@ -541,7 +663,7 @@ static bool rap_send_hci_cs_procedure_enable(struct cs_state_machine *sm,
 
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.handle = cpu_to_le16(handle);
-	cmd.config_id = 0x00; /* Default config Id */
+	cmd.config_id = sm->cs_config.config_id[0];
 	cmd.enable = enable_proc ? 0x01 : 0x00;
 
 	status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_PROC_ENABLE,
@@ -591,6 +713,8 @@ static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
 {
 	struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
 	const struct bt_hci_evt_le_cs_rd_rem_fae_complete *evt;
+	struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete tmp_ev;
+	struct rap_conn_mapping *m;
 	struct iovec iov;
 	int i;
 
@@ -598,11 +722,9 @@ static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
 		size < sizeof(struct bt_hci_evt_le_cs_rd_rem_fae_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) {
@@ -612,21 +734,20 @@ static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
 
 	DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
 
-	/* Check status */
 	if (evt->status != 0) {
 		/* Status 0x11 (Unsupported Feature or Parameter Value) means
 		 * the remote has zero FAE, the procedure continues
 		 * to the Default Settings step.
 		 */
 		if (evt->status == 0x11) {
-			DBG("Remote FAE=0 (No_FAE), proceed to Def Settings");
-			if (is_initiator_role(sm)) {
-				struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete
-								tmp_ev;
+			m = find_mapping_by_handle(sm, evt->handle);
 
+			DBG("Remote FAE=0 (No_FAE), proceed to Def Settings");
+			if (is_initiator_role(sm) ||
+			    (is_both_role(sm) && m && m->is_central)) {
 				memset(&tmp_ev, 0, sizeof(tmp_ev));
 				tmp_ev.handle = evt->handle;
-				DBG("Initiator: send def settings (No_FAE)");
+				DBG("Init/Both: send def settings (No_FAE)");
 				rap_send_hci_def_settings_command(sm, &tmp_ev);
 			} else {
 				DBG("Reflector role: continuing after No_FAE");
@@ -651,13 +772,13 @@ static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
 	}
 
 	/* After receiving FAE Table, send default settings */
-	/* Local capabilities already read before this event */
-	if (is_initiator_role(sm)) {
-		struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete tmp_ev;
+	m = find_mapping_by_handle(sm, evt->handle);
 
+	if (is_initiator_role(sm) ||
+	    (is_both_role(sm) && m && m->is_central)) {
 		memset(&tmp_ev, 0, sizeof(tmp_ev));
 		tmp_ev.handle = evt->handle;
-		DBG("Initiator role: send def settings after FAE table");
+		DBG("Init/Both: send def settings after FAE");
 		rap_send_hci_def_settings_command(sm, &tmp_ev);
 	} else {
 		DBG("Reflector role: Proceeding after FAE Table");
@@ -698,17 +819,16 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 {
 	struct cs_state_machine *sm = user_data;
 	const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *evt;
+	struct rap_conn_mapping *m;
 	struct iovec iov;
 	uint16_t subfeatures_supported;
 
 	if (!sm || !data || size < sizeof(*evt))
 		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) {
@@ -718,7 +838,6 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 
 	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);
@@ -738,16 +857,17 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
 	subfeatures_supported = le16_to_cpu(evt->subfeatures_supported);
 	DBG("subfeatures_supported=0x%04X", subfeatures_supported);
 
-	/* Check Bit 1 of subfeatures_supported (0x0002) */
 	if (!(subfeatures_supported & 0x0002)) {
 		DBG("Bit 1 not set, sending Read Remote FAE Table");
 		bt_rap_read_remote_fae_table(sm, evt->handle);
 		return;
 	}
 
-	/* Local capabilities already read before this event */
-	if (is_initiator_role(sm)) {
-		DBG("Initiator role: send def settings cmd for handle 0x%04X",
+	m = find_mapping_by_handle(sm, evt->handle);
+
+	if (is_initiator_role(sm) ||
+	    (is_both_role(sm) && m && m->is_central)) {
+		DBG("Init/Both: send def settings for 0x%04X",
 			evt->handle);
 		rap_send_hci_def_settings_command(sm, evt);
 	} else {
@@ -763,25 +883,23 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
 	struct cs_state_machine *sm = user_data;
 	const struct bt_hci_evt_le_cs_config_complete *evt;
 	struct rap_ev_cs_config_cmplt rap_ev;
+	struct rap_conn_mapping *mapping;
 	struct iovec iov;
 
 	if (!sm || !data || size < sizeof(*evt))
 		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");
@@ -855,9 +973,6 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
 
 	/* CS Security Enable may only be issued by the BLE Central */
 	if (rap_ev.role == 0x00) {
-		/* Initiator role */
-		struct rap_conn_mapping *mapping;
-
 		mapping = find_mapping_by_handle(sm, evt->handle);
 		if (!mapping || !mapping->is_central) {
 			error("CS Security Enable skipped: not BLE Central");
@@ -980,8 +1095,8 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
 
 	DBG("size=0x%02X", size);
 
-	/* State Check */
-	if (cs_get_current_state(sm) != CS_STATE_WAIT_PROC_CMPLT) {
+	if ((cs_get_current_state(sm) != CS_STATE_WAIT_PROC_CMPLT) &&
+		(cs_get_current_state(sm) != CS_STATE_STARTED)) {
 		DBG("Event received in Wrong State!! ");
 		DBG("Expected : CS_STATE_WAIT_PROC_CMPLT");
 		return;
@@ -1002,6 +1117,8 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
 			evt->status);
 		cs_set_state(sm, CS_STATE_STOPPED);
 		sm->procedure_active = false;
+		if (sm->proc_active_func)
+			sm->proc_active_func(false, sm->proc_active_data);
 		return;
 	}
 
@@ -1030,9 +1147,13 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
 	if (rap_ev.state == 0x01) {
 		cs_set_state(sm, CS_STATE_STARTED);
 		sm->procedure_active = true;
+		if (sm->proc_active_func)
+			sm->proc_active_func(true, sm->proc_active_data);
 	} else if (rap_ev.state == 0x00) {
 		cs_set_state(sm, CS_STATE_STOPPED);
 		sm->procedure_active = false;
+		if (sm->proc_active_func)
+			sm->proc_active_func(false, sm->proc_active_data);
 	}
 
 	/* Send callback to RAP Profile */
@@ -1621,11 +1742,9 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
 
 	queue_push_tail(sm->event_ids, UINT_TO_PTR(id));
 
-	DBG("CS options: role=%u, cs_sync_ant_sel=%u, max_tx_power=%d",
-		role, cs_sync_ant_sel, max_tx_power);
-
-	/* place holder, need DBus API to be called */
-	bt_rap_read_local_supported_capabilities(sm);
+	DBG("HCI state machine attached (role=%u, ant=0x%02x, power=%d)",
+		sm->role_enable, sm->cs_opt.cs_sync_ant_sel,
+		sm->cs_opt.max_tx_power);
 
 	return sm;
 
@@ -1638,6 +1757,73 @@ fail:
 	return NULL;
 }
 
+static bool measurement_timeout_cb(void *user_data)
+{
+	struct cs_state_machine *sm = user_data;
+
+	sm->measurement_timeout_id = 0;
+
+	DBG("Measurement duration expired, stopping CS procedure");
+	bt_rap_stop_measurement(sm);
+
+	if (sm->timeout_func)
+		sm->timeout_func(sm->timeout_data);
+
+	return false;
+}
+
+bool bt_rap_start_measurement(void *hci_sm, uint16_t conn_handle,
+			      uint32_t duration_secs)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !sm->hci)
+		return false;
+
+	if (!find_mapping_by_handle(sm, conn_handle)) {
+		error("start_measurement: hndl 0x%04X not found", conn_handle);
+		return false;
+	}
+
+	sm->active_conn_handle = conn_handle;
+
+	if (sm->measurement_timeout_id) {
+		timeout_remove(sm->measurement_timeout_id);
+		sm->measurement_timeout_id = 0;
+	}
+
+	if (!bt_rap_read_local_supported_capabilities(sm))
+		return false;
+
+	if (duration_secs > 0)
+		sm->measurement_timeout_id = timeout_add_seconds(duration_secs,
+					measurement_timeout_cb, sm, NULL);
+
+	return true;
+}
+
+bool bt_rap_stop_measurement(void *hci_sm)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm || !sm->hci)
+		return false;
+
+	if (sm->measurement_timeout_id) {
+		timeout_remove(sm->measurement_timeout_id);
+		sm->measurement_timeout_id = 0;
+	}
+
+	if (!find_mapping_by_handle(sm, sm->active_conn_handle)) {
+		error("stop_measurement: handle 0x%04X not found",
+		      sm->active_conn_handle);
+		return false;
+	}
+
+	return rap_send_hci_cs_procedure_enable(sm, sm->active_conn_handle,
+						false);
+}
+
 bool bt_rap_set_conn_hndl(void *hci_sm, struct bt_rap *rap,
 		uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
 		bool is_central)
@@ -1672,6 +1858,40 @@ void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle)
 
 	DBG("Clearing connection mapping: handle=0x%04X", handle);
 	remove_conn_mapping(sm, handle);
+	sm->procedure_active = false;
+}
+
+bool bt_rap_is_procedure_active(void *hci_sm)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm)
+		return false;
+
+	return sm->procedure_active;
+}
+
+void bt_rap_set_timeout_cb(void *hci_sm, void (*func)(void *), void *user_data)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm)
+		return;
+
+	sm->timeout_func = func;
+	sm->timeout_data = user_data;
+}
+
+void bt_rap_set_proc_active_cb(void *hci_sm, void (*func)(bool, void *),
+				void *user_data)
+{
+	struct cs_state_machine *sm = hci_sm;
+
+	if (!sm)
+		return;
+
+	sm->proc_active_func = func;
+	sm->proc_active_data = user_data;
 }
 
 void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm)
@@ -1690,12 +1910,14 @@ void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm)
 			queue_foreach(sm->event_ids, unregister_event_id,
 								sm->hci);
 
+		if (sm->measurement_timeout_id) {
+			timeout_remove(sm->measurement_timeout_id);
+			sm->measurement_timeout_id = 0;
+		}
+
 		queue_destroy(sm->event_ids, NULL);
 
-		/* Clean up per-instance connection mappings.
-		 * Each state machine owns its own conn_mappings queue;
-		 * this destroys all mappings for this instance only.
-		 */
+		/* destroys mappings for this instance only */
 		queue_destroy(sm->conn_mappings, mapping_free);
 
 		/* Free the state machine */
-- 


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

* [PATCH BlueZ v1 5/5] client: Add Channel Sounding shell submenu
  2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
                   ` (3 preceding siblings ...)
  2026-07-06 16:16 ` [PATCH BlueZ v1 4/5] profiles: Add D-Bus Channel Sounding control API Naga Bhavani Akella
@ 2026-07-06 16:16 ` Naga Bhavani Akella
  4 siblings, 0 replies; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-06 16:16 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
	Naga Bhavani Akella

Add a cs bt_shell submenu for controlling
org.bluez.ChannelSounding1, supporting measurement start/stop,
default-setting configuration, and session status display.
Maintain reusable validated CS parameters and
track active sessions via D-Bus Active state,
replies, and disconnects.
Integrate with client/main.c to manage per-adapter
ChannelSounding proxies, device resolution,
property updates, and session cleanup.
---
 Makefile.tools |   3 +-
 client/cs.c    | 981 +++++++++++++++++++++++++++++++++++++++++++++++++
 client/cs.h    |  18 +
 client/main.c  |  73 +++-
 4 files changed, 1073 insertions(+), 2 deletions(-)
 create mode 100644 client/cs.c
 create mode 100644 client/cs.h

diff --git a/Makefile.tools b/Makefile.tools
index 6188449f1..b5f7ef8da 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -16,7 +16,8 @@ client_bluetoothctl_SOURCES = client/main.c \
 					client/mgmt.h client/mgmt.c \
 					client/assistant.h client/assistant.c \
 					client/hci.h client/hci.c \
-					client/telephony.h client/telephony.c
+					client/telephony.h client/telephony.c \
+					client/cs.h client/cs.c
 client_bluetoothctl_LDADD = lib/libbluetooth-internal.la \
 			gdbus/libgdbus-internal.la src/libshared-glib.la \
 			$(GLIB_LIBS) $(DBUS_LIBS) -lreadline
diff --git a/client/cs.c b/client/cs.c
new file mode 100644
index 000000000..00b64cb77
--- /dev/null
+++ b/client/cs.c
@@ -0,0 +1,981 @@
+// 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "src/shared/shell.h"
+#include "cs.h"
+
+static GList *cs_proxies;
+static GList **cs_device_list;
+
+/* ---- Per-device active session ---- */
+
+struct cs_session {
+	GDBusProxy *proxy;
+};
+
+static GList *cs_sessions;
+
+static struct cs_session *cs_find_session(GDBusProxy *proxy);
+
+/* ---- Parameter state (used to build the StartMeasurement dict) ---- */
+
+static uint8_t cs_role        = 0x03;
+static uint8_t cs_sync_ant    = 0xFF;
+static int8_t  cs_max_tx_power = 0x14;
+
+static struct {
+	uint8_t config_id;
+	uint8_t main_mode_type;
+	uint8_t sub_mode_type;
+	uint8_t main_mode_min_steps;
+	uint8_t main_mode_max_steps;
+	uint8_t main_mode_repetition;
+	uint8_t mode0_steps;
+	uint8_t role;
+	uint8_t rtt_types;
+	uint8_t cs_sync_phy;
+	uint8_t channel_map[10];
+	uint8_t channel_map_repetition;
+	uint8_t channel_selection_type;
+	uint8_t channel_shape;
+	uint8_t channel_jump;
+	uint8_t companion_signal_enable;
+} cs_cfg = {
+	.config_id               = 0x00,
+	.main_mode_type          = 0x01,
+	.sub_mode_type           = 0xFF,
+	.main_mode_min_steps     = 0x02,
+	.main_mode_max_steps     = 0x03,
+	.main_mode_repetition    = 0x01,
+	.mode0_steps             = 0x02,
+	.role                    = 0x00,
+	.rtt_types               = 0x00,
+	.cs_sync_phy             = 0x01,
+	.channel_map             = { 0xFC, 0xFF, 0x7F, 0xFC, 0xFF,
+				     0xFF, 0xFF, 0xFF, 0xFF, 0x1F },
+	.channel_map_repetition  = 0x01,
+	.channel_selection_type  = 0x00,
+	.channel_shape           = 0x00,
+	.channel_jump            = 0x02,
+	.companion_signal_enable = 0x00,
+};
+
+static struct {
+	uint16_t max_procedure_duration;
+	uint16_t min_period_between_procedures;
+	uint16_t max_period_between_procedures;
+	uint16_t max_procedure_count;
+	uint8_t  min_sub_event_len[3];
+	uint8_t  max_sub_event_len[3];
+	uint8_t  tone_antenna_config_selection;
+	uint8_t  phy;
+	uint8_t  tx_power_delta;
+	uint8_t  preferred_peer_antenna;
+	uint8_t  snr_control_initiator;
+	uint8_t  snr_control_reflector;
+} cs_freq = {
+	.max_procedure_duration        = 0x0640,
+	.min_period_between_procedures = 0x001E,
+	.max_period_between_procedures = 0x0096,
+	.max_procedure_count           = 0x0000,
+	.min_sub_event_len             = { 0x00, 0x20, 0x00 },
+	.max_sub_event_len             = { 0x03, 0x20, 0x00 },
+	.tone_antenna_config_selection = 0x07,
+	.phy                           = 0x01,
+	.tx_power_delta                = 0x80,
+	.preferred_peer_antenna        = 0x03,
+	.snr_control_initiator         = 0xFF,
+	.snr_control_reflector         = 0xFF,
+};
+
+void cs_proxy_added(GDBusProxy *proxy)
+{
+	cs_proxies = g_list_append(cs_proxies, proxy);
+}
+
+void cs_proxy_removed(GDBusProxy *proxy)
+{
+	struct cs_session *s;
+	GList *list;
+
+	cs_proxies = g_list_remove(cs_proxies, proxy);
+
+	for (list = g_list_first(cs_sessions); list;
+	     list = g_list_next(list)) {
+		s = list->data;
+
+		if (s->proxy == proxy) {
+			cs_sessions = g_list_remove(cs_sessions, s);
+			g_free(s);
+			break;
+		}
+	}
+}
+
+/* Drop any active CS session for a device proxy path on disconnect. */
+void cs_device_disconnected(const char *dev_path)
+{
+	struct cs_session *s;
+	GList *list;
+
+	for (list = g_list_first(cs_sessions); list;
+	     list = g_list_next(list)) {
+		s = list->data;
+
+		if (!strcmp(g_dbus_proxy_get_path(s->proxy), dev_path)) {
+			bt_shell_printf("Measurement stopped (disconnected)"
+					" on %s\n", dev_path);
+			cs_sessions = g_list_remove(cs_sessions, s);
+			g_free(s);
+			break;
+		}
+	}
+}
+
+/* Drop active CS session when the daemon reports the measurement stopped. */
+void cs_measurement_stopped(GDBusProxy *proxy)
+{
+	struct cs_session *s = cs_find_session(proxy);
+
+	if (!s)
+		return;
+
+	bt_shell_printf("Measurement stopped (timer expired) on %s\n",
+			g_dbus_proxy_get_path(proxy));
+	cs_sessions = g_list_remove(cs_sessions, s);
+	g_free(s);
+}
+
+/* Record a measurement started by the remote side (Reflector role). */
+void cs_measurement_started(GDBusProxy *proxy)
+{
+	struct cs_session *s;
+
+	if (cs_find_session(proxy))
+		return;
+
+	s = g_new0(struct cs_session, 1);
+	s->proxy = proxy;
+	cs_sessions = g_list_append(cs_sessions, s);
+
+	bt_shell_printf("Measurement started on %s\n",
+			g_dbus_proxy_get_path(proxy));
+}
+
+void cs_set_device_list(GList **devices)
+{
+	cs_device_list = devices;
+}
+
+/* Resolve a Bluetooth address to the device's D-Bus object path. */
+static const char *cs_resolve_address(const char *address)
+{
+	GDBusProxy *proxy;
+	DBusMessageIter iter;
+	const char *str;
+	GList *list;
+
+	if (!cs_device_list) {
+		bt_shell_printf("DBG: cs_device_list is NULL\n");
+		return NULL;
+	}
+
+	if (!*cs_device_list) {
+		bt_shell_printf("DBG: *cs_device_list is empty\n");
+		return NULL;
+	}
+
+	for (list = g_list_first(*cs_device_list); list;
+	     list = g_list_next(list)) {
+		proxy = list->data;
+
+		if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) {
+			bt_shell_printf("DBG: proxy %s has no Address\n",
+					g_dbus_proxy_get_path(proxy));
+			continue;
+		}
+
+		dbus_message_iter_get_basic(&iter, &str);
+
+		if (!strcasecmp(str, address))
+			return g_dbus_proxy_get_path(proxy);
+	}
+
+	return NULL;
+}
+
+/* Find the ChannelSounding1 proxy for the device at the given object path. */
+static GDBusProxy *cs_find_proxy(const char *dev_path)
+{
+	GDBusProxy *proxy;
+	const char *path;
+	GList *list;
+
+	if (!cs_proxies) {
+		bt_shell_printf("DBG: cs_proxies is empty\n");
+		return NULL;
+	}
+
+	for (list = g_list_first(cs_proxies); list;
+	     list = g_list_next(list)) {
+		proxy = list->data;
+		path = g_dbus_proxy_get_path(proxy);
+
+		if (!strcmp(path, dev_path))
+			return proxy;
+	}
+
+	return NULL;
+}
+
+static struct cs_session *cs_find_session(GDBusProxy *proxy)
+{
+	struct cs_session *s;
+	GList *list;
+
+	for (list = g_list_first(cs_sessions); list;
+	     list = g_list_next(list)) {
+		s = list->data;
+
+		if (s->proxy == proxy)
+			return s;
+	}
+
+	return NULL;
+}
+
+/* ---- dict helpers ---- */
+
+static void dict_append_byte(DBusMessageIter *dict, const char *key,
+			     uint8_t val)
+{
+	DBusMessageIter entry, variant;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+					 NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					 DBUS_TYPE_BYTE_AS_STRING, &variant);
+	dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &val);
+	dbus_message_iter_close_container(&entry, &variant);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_uint16(DBusMessageIter *dict, const char *key,
+			       uint16_t val)
+{
+	DBusMessageIter entry, variant;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+					 NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					 "q", &variant);
+	dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT16, &val);
+	dbus_message_iter_close_container(&entry, &variant);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_uint32(DBusMessageIter *dict, const char *key,
+			       uint32_t val)
+{
+	DBusMessageIter entry, variant;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+					 NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					 "u", &variant);
+	dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &val);
+	dbus_message_iter_close_container(&entry, &variant);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_byte_array(DBusMessageIter *dict, const char *key,
+				   const uint8_t *bytes, int len)
+{
+	DBusMessageIter entry, variant, array;
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+					 NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+					 "ay", &variant);
+	dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+					 DBUS_TYPE_BYTE_AS_STRING, &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+					     &bytes, len);
+	dbus_message_iter_close_container(&variant, &array);
+	dbus_message_iter_close_container(&entry, &variant);
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+/* ---- start ---- */
+
+static uint32_t pending_duration_secs;
+static const char *pending_dev_path;
+
+static void start_measurement_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+	uint8_t tx = (uint8_t)cs_max_tx_power;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					 "{sv}", &dict);
+
+	/* timing */
+	dict_append_uint32(&dict, "duration_secs", pending_duration_secs);
+
+	/* default settings */
+	dict_append_byte(&dict, "role",            cs_role);
+	dict_append_byte(&dict, "cs_sync_ant_sel", cs_sync_ant);
+	dict_append_byte(&dict, "max_tx_power",    tx);
+
+	/* CS config */
+	dict_append_byte(&dict, "config_id",
+			 cs_cfg.config_id);
+	dict_append_byte(&dict, "main_mode_type",
+			 cs_cfg.main_mode_type);
+	dict_append_byte(&dict, "sub_mode_type",
+			 cs_cfg.sub_mode_type);
+	dict_append_byte(&dict, "main_mode_min_steps",
+			 cs_cfg.main_mode_min_steps);
+	dict_append_byte(&dict, "main_mode_max_steps",
+			 cs_cfg.main_mode_max_steps);
+	dict_append_byte(&dict, "main_mode_repetition",
+			 cs_cfg.main_mode_repetition);
+	dict_append_byte(&dict, "mode0_steps",
+			 cs_cfg.mode0_steps);
+	dict_append_byte(&dict, "rtt_types",
+			 cs_cfg.rtt_types);
+	dict_append_byte(&dict, "cs_sync_phy",
+			 cs_cfg.cs_sync_phy);
+	dict_append_byte_array(&dict, "channel_map",
+			       cs_cfg.channel_map, 10);
+	dict_append_byte(&dict, "channel_map_repetition",
+			 cs_cfg.channel_map_repetition);
+	dict_append_byte(&dict, "channel_selection_type",
+			 cs_cfg.channel_selection_type);
+	dict_append_byte(&dict, "channel_shape",
+			 cs_cfg.channel_shape);
+	dict_append_byte(&dict, "channel_jump",
+			 cs_cfg.channel_jump);
+	dict_append_byte(&dict, "companion_signal_enable",
+			 cs_cfg.companion_signal_enable);
+
+	/* CS frequency */
+	dict_append_uint16(&dict, "max_procedure_duration",
+			   cs_freq.max_procedure_duration);
+	dict_append_uint16(&dict, "min_period_between_procedures",
+			   cs_freq.min_period_between_procedures);
+	dict_append_uint16(&dict, "max_period_between_procedures",
+			   cs_freq.max_period_between_procedures);
+	dict_append_uint16(&dict, "max_procedure_count",
+			   cs_freq.max_procedure_count);
+	dict_append_byte_array(&dict, "min_sub_event_len",
+			       cs_freq.min_sub_event_len, 3);
+	dict_append_byte_array(&dict, "max_sub_event_len",
+			       cs_freq.max_sub_event_len, 3);
+	dict_append_byte(&dict, "tone_antenna_config_selection",
+			 cs_freq.tone_antenna_config_selection);
+	dict_append_byte(&dict, "phy",
+			 cs_freq.phy);
+	dict_append_byte(&dict, "tx_power_delta",
+			 cs_freq.tx_power_delta);
+	dict_append_byte(&dict, "preferred_peer_antenna",
+			 cs_freq.preferred_peer_antenna);
+	dict_append_byte(&dict, "snr_control_initiator",
+			 cs_freq.snr_control_initiator);
+	dict_append_byte(&dict, "snr_control_reflector",
+			 cs_freq.snr_control_reflector);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+/* ---- cs_set_param: apply name=value to the local parameter state ---- */
+
+static bool cs_set_param(const char *name, const char *valstr)
+{
+	uint8_t map[10];
+	uint8_t buf[3];
+	const char *str;
+	char *endptr;
+	unsigned long uval;
+	long sval;
+	int i;
+
+	/* Byte-array: channel_map — 10 colon-separated hex bytes */
+	if (strcmp(name, "channel_map") == 0) {
+		str = valstr;
+		i = 0;
+
+		while (i < 10) {
+			uval = strtoul(str, &endptr, 16);
+			if (endptr == str || uval > 0xFF) {
+				bt_shell_printf("channel_map: invalid byte"
+						" at index %d\n", i);
+				return false;
+			}
+			map[i++] = (uint8_t)uval;
+			if (*endptr == '\0')
+				break;
+			if (*endptr != ':') {
+				bt_shell_printf("channel_map: expected ':'"
+						" separator\n");
+				return false;
+			}
+			str = endptr + 1;
+		}
+		if (i != 10) {
+			bt_shell_printf("channel_map: need exactly 10"
+					" colon-separated hex bytes\n");
+			return false;
+		}
+		memcpy(cs_cfg.channel_map, map, 10);
+		return true;
+	}
+
+	/* Byte-array: min/max_sub_event_len — 3 colon-separated hex bytes */
+	if (strcmp(name, "min_sub_event_len") == 0 ||
+	    strcmp(name, "max_sub_event_len") == 0) {
+		str = valstr;
+		i = 0;
+
+		while (i < 3) {
+			uval = strtoul(str, &endptr, 16);
+			if (endptr == str || uval > 0xFF) {
+				bt_shell_printf("%s: invalid byte at index"
+						" %d\n", name, i);
+				return false;
+			}
+			buf[i++] = (uint8_t)uval;
+			if (*endptr == '\0')
+				break;
+			if (*endptr != ':') {
+				bt_shell_printf("%s: expected ':' separator\n",
+						name);
+				return false;
+			}
+			str = endptr + 1;
+		}
+		if (i != 3) {
+			bt_shell_printf("%s: need exactly 3 colon-separated"
+					" hex bytes\n", name);
+			return false;
+		}
+		if (strcmp(name, "min_sub_event_len") == 0)
+			memcpy(cs_freq.min_sub_event_len, buf, 3);
+		else
+			memcpy(cs_freq.max_sub_event_len, buf, 3);
+		return true;
+	}
+
+	/* max_tx_power is signed (-127 to +20 dBm) */
+	if (strcmp(name, "max_tx_power") == 0) {
+		sval = strtol(valstr, &endptr, 0);
+
+		if (*endptr != '\0' || sval < -127 || sval > 20) {
+			bt_shell_printf("max_tx_power: valid range -127"
+					" to 20 dBm\n");
+			return false;
+		}
+		cs_max_tx_power = (int8_t)sval;
+		return true;
+	}
+
+	/* All remaining parameters are unsigned */
+	uval = strtoul(valstr, &endptr, 0);
+	if (*endptr != '\0') {
+		bt_shell_printf("%s: invalid value '%s'\n", name, valstr);
+		return false;
+	}
+
+	if (strcmp(name, "role") == 0) {
+		if (uval < 0x01 || uval > 0x03) {
+			bt_shell_printf("role: 0x01=Initiator 0x02=Reflector"
+					" 0x03=Both\n");
+			return false;
+		}
+		cs_role = (uint8_t)uval;
+	} else if (strcmp(name, "cs_sync_ant_sel") == 0) {
+		cs_sync_ant = (uint8_t)uval;
+	} else if (strcmp(name, "config_id") == 0) {
+		cs_cfg.config_id = (uint8_t)uval;
+	} else if (strcmp(name, "main_mode_type") == 0) {
+		cs_cfg.main_mode_type = (uint8_t)uval;
+	} else if (strcmp(name, "sub_mode_type") == 0) {
+		cs_cfg.sub_mode_type = (uint8_t)uval;
+	} else if (strcmp(name, "main_mode_min_steps") == 0) {
+		cs_cfg.main_mode_min_steps = (uint8_t)uval;
+	} else if (strcmp(name, "main_mode_max_steps") == 0) {
+		cs_cfg.main_mode_max_steps = (uint8_t)uval;
+	} else if (strcmp(name, "main_mode_repetition") == 0) {
+		cs_cfg.main_mode_repetition = (uint8_t)uval;
+	} else if (strcmp(name, "mode0_steps") == 0) {
+		cs_cfg.mode0_steps = (uint8_t)uval;
+	} else if (strcmp(name, "rtt_types") == 0) {
+		cs_cfg.rtt_types = (uint8_t)uval;
+	} else if (strcmp(name, "cs_sync_phy") == 0) {
+		if (uval != 0x01 && uval != 0x02) {
+			bt_shell_printf("cs_sync_phy: 0x01=LE 1M"
+					" 0x02=LE 2M\n");
+			return false;
+		}
+		cs_cfg.cs_sync_phy = (uint8_t)uval;
+	} else if (strcmp(name, "channel_map_repetition") == 0) {
+		cs_cfg.channel_map_repetition = (uint8_t)uval;
+	} else if (strcmp(name, "channel_selection_type") == 0) {
+		cs_cfg.channel_selection_type = (uint8_t)uval;
+	} else if (strcmp(name, "channel_shape") == 0) {
+		cs_cfg.channel_shape = (uint8_t)uval;
+	} else if (strcmp(name, "channel_jump") == 0) {
+		cs_cfg.channel_jump = (uint8_t)uval;
+	} else if (strcmp(name, "companion_signal_enable") == 0) {
+		if (uval > 1) {
+			bt_shell_printf("companion_signal_enable: 0 or 1\n");
+			return false;
+		}
+		cs_cfg.companion_signal_enable = (uint8_t)uval;
+	} else if (strcmp(name, "max_procedure_duration") == 0) {
+		cs_freq.max_procedure_duration = (uint16_t)uval;
+	} else if (strcmp(name, "min_period_between_procedures") == 0) {
+		cs_freq.min_period_between_procedures = (uint16_t)uval;
+	} else if (strcmp(name, "max_period_between_procedures") == 0) {
+		cs_freq.max_period_between_procedures = (uint16_t)uval;
+	} else if (strcmp(name, "max_procedure_count") == 0) {
+		cs_freq.max_procedure_count = (uint16_t)uval;
+	} else if (strcmp(name, "tone_antenna_config_selection") == 0) {
+		cs_freq.tone_antenna_config_selection = (uint8_t)uval;
+	} else if (strcmp(name, "phy") == 0) {
+		if (uval != 0x01 && uval != 0x02) {
+			bt_shell_printf("phy: 0x01=LE 1M  0x02=LE 2M\n");
+			return false;
+		}
+		cs_freq.phy = (uint8_t)uval;
+	} else if (strcmp(name, "tx_power_delta") == 0) {
+		cs_freq.tx_power_delta = (uint8_t)uval;
+	} else if (strcmp(name, "preferred_peer_antenna") == 0) {
+		cs_freq.preferred_peer_antenna = (uint8_t)uval;
+	} else if (strcmp(name, "snr_control_initiator") == 0) {
+		cs_freq.snr_control_initiator = (uint8_t)uval;
+	} else if (strcmp(name, "snr_control_reflector") == 0) {
+		cs_freq.snr_control_reflector = (uint8_t)uval;
+	} else {
+		bt_shell_printf("Unknown parameter: %s\n", name);
+		return false;
+	}
+
+	return true;
+}
+
+static void start_measurement_reply(DBusMessage *message, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	struct cs_session *s;
+	DBusError error;
+
+	dbus_error_init(&error);
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("StartMeasurement failed: %s\n", error.message);
+		dbus_error_free(&error);
+		return;
+	}
+
+	s = g_new0(struct cs_session, 1);
+	s->proxy    = proxy;
+	cs_sessions = g_list_append(cs_sessions, s);
+
+	bt_shell_printf("Measurement started on %s\n",
+			g_dbus_proxy_get_path(proxy));
+}
+
+static void cmd_cs_start(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+	char name[64];
+	char *endptr;
+	char *eq;
+	size_t nlen;
+	unsigned long val;
+	int positional = 0;
+	int i;
+
+	pending_duration_secs = 0;
+	pending_dev_path = NULL;
+
+	for (i = 1; i < argc; i++) {
+		eq = strchr(argv[i], '=');
+
+		if (eq) {
+			nlen = (size_t)(eq - argv[i]);
+
+			if (nlen == 0 || nlen >= sizeof(name)) {
+				bt_shell_printf("Invalid param: %s\n", argv[i]);
+				return;
+			}
+			memcpy(name, argv[i], nlen);
+			name[nlen] = '\0';
+			if (!cs_set_param(name, eq + 1))
+				return;
+		} else {
+			if (positional == 0) {
+				pending_dev_path = cs_resolve_address(argv[i]);
+				if (!pending_dev_path) {
+					bt_shell_printf("Device %s not"
+							" found\n", argv[i]);
+					return;
+				}
+			} else if (positional == 1) {
+				val = strtoul(argv[i], &endptr, 0);
+
+				if (*endptr != '\0') {
+					bt_shell_printf("Invalid duration:"
+							" %s\n", argv[i]);
+					return;
+				}
+				pending_duration_secs = (uint32_t)val;
+			} else {
+				bt_shell_printf("Too many positional"
+						" arguments\n");
+				return;
+			}
+			positional++;
+		}
+	}
+
+	if (pending_dev_path) {
+		proxy = cs_find_proxy(pending_dev_path);
+		if (!proxy) {
+			bt_shell_printf("No ChannelSounding1 interface for"
+					" that device\n");
+			return;
+		}
+	} else {
+		if (!cs_proxies) {
+			bt_shell_printf("No ChannelSounding1 interface"
+					" available\n");
+			return;
+		}
+		proxy = cs_proxies->data;
+	}
+
+	if (cs_find_session(proxy)) {
+		bt_shell_printf("Measurement already active on %s\n",
+				g_dbus_proxy_get_path(proxy));
+		return;
+	}
+
+	if (!g_dbus_proxy_method_call(proxy, "StartMeasurement",
+				      start_measurement_setup,
+				      start_measurement_reply, proxy, NULL))
+		bt_shell_printf("Failed to send StartMeasurement\n");
+}
+
+/* ---- defset ---- */
+
+static void defset_setup(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter dict;
+	uint8_t tx = (uint8_t)cs_max_tx_power;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+	dict_append_byte(&dict, "role",            cs_role);
+	dict_append_byte(&dict, "cs_sync_ant_sel", cs_sync_ant);
+	dict_append_byte(&dict, "max_tx_power",    tx);
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void defset_reply(DBusMessage *message, void *user_data)
+{
+	GDBusProxy *proxy = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("SetDefaultSettings failed: %s\n",
+				error.message);
+		dbus_error_free(&error);
+		return;
+	}
+
+	bt_shell_printf("Default settings applied on %s\n",
+			g_dbus_proxy_get_path(proxy));
+}
+
+static void cmd_cs_defset(int argc, char *argv[])
+{
+	const char *dev_path;
+	GDBusProxy *proxy;
+	char name[64];
+	size_t nlen;
+	char *eq;
+	int i;
+
+	/* First positional arg (no '=') is an optional device address. */
+	i = 1;
+	if (i < argc && !strchr(argv[i], '=')) {
+		dev_path = cs_resolve_address(argv[i]);
+
+		if (!dev_path) {
+			bt_shell_printf("Device %s not found\n", argv[i]);
+			return;
+		}
+		proxy = cs_find_proxy(dev_path);
+		if (!proxy) {
+			bt_shell_printf("No ChannelSounding1 interface for"
+					" that device\n");
+			return;
+		}
+		i++;
+	} else {
+		if (!cs_proxies) {
+			bt_shell_printf("No ChannelSounding1 interface"
+					" available\n");
+			return;
+		}
+		proxy = cs_proxies->data;
+	}
+
+	/* Remaining args must be param=value for the three default-settings
+	 * fields: role, cs_sync_ant_sel, max_tx_power.
+	 */
+	for (; i < argc; i++) {
+		eq = strchr(argv[i], '=');
+
+		if (!eq) {
+			bt_shell_printf("Expected param=value, got: %s\n",
+					argv[i]);
+			return;
+		}
+		nlen = (size_t)(eq - argv[i]);
+		if (nlen == 0 || nlen >= sizeof(name)) {
+			bt_shell_printf("Invalid param: %s\n", argv[i]);
+			return;
+		}
+		memcpy(name, argv[i], nlen);
+		name[nlen] = '\0';
+
+		if (strcmp(name, "role") != 0 &&
+		    strcmp(name, "cs_sync_ant_sel") != 0 &&
+		    strcmp(name, "max_tx_power") != 0) {
+			bt_shell_printf("defset: unknown param '%s' "
+					"(role, cs_sync_ant_sel, max_tx_power)\n",
+					name);
+			return;
+		}
+		if (!cs_set_param(name, eq + 1))
+			return;
+	}
+
+	if (!g_dbus_proxy_method_call(proxy, "SetDefaultSettings",
+				      defset_setup, defset_reply, proxy, NULL))
+		bt_shell_printf("Failed to send SetDefaultSettings\n");
+}
+
+/* ---- stop ---- */
+
+static void stop_measurement_reply(DBusMessage *message, void *user_data)
+{
+	struct cs_session *s = user_data;
+	DBusError error;
+
+	dbus_error_init(&error);
+	if (dbus_set_error_from_message(&error, message)) {
+		bt_shell_printf("StopMeasurement failed: %s\n", error.message);
+		dbus_error_free(&error);
+		return;
+	}
+
+	bt_shell_printf("Measurement stopped on %s\n",
+			g_dbus_proxy_get_path(s->proxy));
+	cs_sessions = g_list_remove(cs_sessions, s);
+	g_free(s);
+}
+
+static void cmd_cs_stop(int argc, char *argv[])
+{
+	const char *dev_path;
+	struct cs_session *s;
+	GDBusProxy *proxy;
+
+	if (argc >= 2) {
+		dev_path = cs_resolve_address(argv[1]);
+
+		if (!dev_path) {
+			bt_shell_printf("Device %s not found\n", argv[1]);
+			return;
+		}
+		proxy = cs_find_proxy(dev_path);
+		if (!proxy) {
+			bt_shell_printf("No ChannelSounding1 interface for"
+					" that device\n");
+			return;
+		}
+		s = cs_find_session(proxy);
+		if (!s) {
+			bt_shell_printf("No active measurement on %s\n",
+					argv[1]);
+			return;
+		}
+	} else {
+		if (!cs_sessions) {
+			bt_shell_printf("No active measurements\n");
+			return;
+		}
+		if (g_list_length(cs_sessions) > 1) {
+			bt_shell_printf("Multiple active measurements —"
+					" specify device address\n");
+			return;
+		}
+		s = cs_sessions->data;
+	}
+
+	if (!g_dbus_proxy_method_call(s->proxy, "StopMeasurement",
+				      NULL,
+				      stop_measurement_reply, s, NULL))
+		bt_shell_printf("Failed to send StopMeasurement\n");
+}
+
+/* ---- show ---- */
+
+static void cmd_cs_show(int argc, char *argv[])
+{
+	struct cs_session *s;
+	size_t j;
+	GList *list;
+
+	bt_shell_printf("Active measurements:\n");
+	if (!cs_sessions) {
+		bt_shell_printf("  none\n");
+	} else {
+		for (list = g_list_first(cs_sessions); list;
+		     list = g_list_next(list)) {
+			s = list->data;
+
+			bt_shell_printf("  %s\n",
+					g_dbus_proxy_get_path(s->proxy));
+		}
+	}
+
+	bt_shell_printf("\n=== Default Settings ===\n");
+	bt_shell_printf("  role           : %u"
+			" (1=Initiator 2=Reflector 3=Both)\n", cs_role);
+	bt_shell_printf("  cs_sync_ant_sel: 0x%02x\n", cs_sync_ant);
+	bt_shell_printf("  max_tx_power   : %d dBm\n", cs_max_tx_power);
+
+	bt_shell_printf("\n=== CS Config Params ===\n");
+	bt_shell_printf("  config_id               : %u\n",
+			cs_cfg.config_id);
+	bt_shell_printf("  main_mode_type          : 0x%02x\n",
+			cs_cfg.main_mode_type);
+	bt_shell_printf("  sub_mode_type           : 0x%02x\n",
+			cs_cfg.sub_mode_type);
+	bt_shell_printf("  main_mode_min_steps     : %u\n",
+			cs_cfg.main_mode_min_steps);
+	bt_shell_printf("  main_mode_max_steps     : %u\n",
+			cs_cfg.main_mode_max_steps);
+	bt_shell_printf("  main_mode_repetition    : %u\n",
+			cs_cfg.main_mode_repetition);
+	bt_shell_printf("  mode0_steps             : %u\n",
+			cs_cfg.mode0_steps);
+	bt_shell_printf("  role                    : %u\n",
+			cs_cfg.role);
+	bt_shell_printf("  rtt_types               : %u\n",
+			cs_cfg.rtt_types);
+	bt_shell_printf("  cs_sync_phy             : %u\n",
+			cs_cfg.cs_sync_phy);
+	bt_shell_printf("  channel_map             :");
+	for (j = 0; j < 10; j++)
+		bt_shell_printf(" %02x", cs_cfg.channel_map[j]);
+	bt_shell_printf("\n");
+	bt_shell_printf("  channel_map_repetition  : %u\n",
+			cs_cfg.channel_map_repetition);
+	bt_shell_printf("  channel_selection_type  : %u\n",
+			cs_cfg.channel_selection_type);
+	bt_shell_printf("  channel_shape           : %u\n",
+			cs_cfg.channel_shape);
+	bt_shell_printf("  channel_jump            : %u\n",
+			cs_cfg.channel_jump);
+	bt_shell_printf("  companion_signal_enable : %u\n",
+			cs_cfg.companion_signal_enable);
+
+	bt_shell_printf("\n=== CS Frequency Params ===\n");
+	bt_shell_printf("  max_procedure_duration        : %u\n",
+			cs_freq.max_procedure_duration);
+	bt_shell_printf("  min_period_between_procedures : %u\n",
+			cs_freq.min_period_between_procedures);
+	bt_shell_printf("  max_period_between_procedures : %u\n",
+			cs_freq.max_period_between_procedures);
+	bt_shell_printf("  max_procedure_count           : %u\n",
+			cs_freq.max_procedure_count);
+	bt_shell_printf("  min_sub_event_len             : %02x %02x %02x\n",
+			cs_freq.min_sub_event_len[0],
+			cs_freq.min_sub_event_len[1],
+			cs_freq.min_sub_event_len[2]);
+	bt_shell_printf("  max_sub_event_len             : %02x %02x %02x\n",
+			cs_freq.max_sub_event_len[0],
+			cs_freq.max_sub_event_len[1],
+			cs_freq.max_sub_event_len[2]);
+	bt_shell_printf("  tone_antenna_config_selection : 0x%02x\n",
+			cs_freq.tone_antenna_config_selection);
+	bt_shell_printf("  phy                           : 0x%02x\n",
+			cs_freq.phy);
+	bt_shell_printf("  tx_power_delta                : 0x%02x\n",
+			cs_freq.tx_power_delta);
+	bt_shell_printf("  preferred_peer_antenna        : 0x%02x\n",
+			cs_freq.preferred_peer_antenna);
+	bt_shell_printf("  snr_control_initiator         : 0x%02x\n",
+			cs_freq.snr_control_initiator);
+	bt_shell_printf("  snr_control_reflector         : 0x%02x\n",
+			cs_freq.snr_control_reflector);
+}
+
+static const struct bt_shell_menu cs_menu = {
+	.name = "cs",
+	.desc = "Channel Sounding Submenu",
+	.entries = {
+	{ "start",  "[dev_addr [duration_secs]] [param=value ...]",
+				cmd_cs_start,
+				"Set param and start distance measurement" },
+	{ "defset", "[param=value ...]",
+				cmd_cs_defset,
+				"Set CS default settings (role, cs_sync_ant_sel,"
+				" max_tx_power) without starting a measurement;"
+				" required for reflector role" },
+	{ "stop",   "[dev_addr]",
+				cmd_cs_stop,
+				"Stop the active distance measurement;"
+				" address required when multiple are active" },
+	{ "show",   NULL,
+				cmd_cs_show,
+				"Show active session id and current"
+				" CS parameters" },
+	{ } },
+};
+
+void cs_add_submenu(void)
+{
+	bt_shell_add_submenu(&cs_menu);
+}
+
+void cs_remove_submenu(void)
+{
+}
diff --git a/client/cs.h b/client/cs.h
new file mode 100644
index 000000000..8902c8143
--- /dev/null
+++ b/client/cs.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include "gdbus/gdbus.h"
+
+void cs_add_submenu(void);
+void cs_remove_submenu(void);
+
+void cs_proxy_added(GDBusProxy *proxy);
+void cs_proxy_removed(GDBusProxy *proxy);
+void cs_device_disconnected(const char *dev_path);
+void cs_measurement_started(GDBusProxy *proxy);
+void cs_measurement_stopped(GDBusProxy *proxy);
+void cs_set_device_list(GList **devices);
diff --git a/client/main.c b/client/main.c
index 9d9ac8b14..80bba4ec5 100644
--- a/client/main.c
+++ b/client/main.c
@@ -39,6 +39,7 @@
 #include "assistant.h"
 #include "hci.h"
 #include "telephony.h"
+#include "cs.h"
 
 /* String display constants */
 #define COLORED_NEW	COLOR_GREEN "NEW" COLOR_OFF
@@ -60,6 +61,7 @@ struct adapter {
 	GList *devices;
 	GList *sets;
 	GList *bearers;
+	GList *cs_proxies;
 };
 
 static struct adapter *default_ctrl;
@@ -124,6 +126,7 @@ static void disconnect_handler(DBusConnection *connection, void *user_data)
 	battery_proxies = NULL;
 
 	default_ctrl = NULL;
+	cs_set_device_list(NULL);
 }
 
 static void print_adapter(GDBusProxy *proxy, const char *description)
@@ -391,8 +394,10 @@ static struct adapter *adapter_new(GDBusProxy *proxy)
 
 	ctrl_list = g_list_append(ctrl_list, adapter);
 
-	if (!default_ctrl)
+	if (!default_ctrl) {
 		default_ctrl = adapter;
+		cs_set_device_list(&adapter->devices);
+	}
 
 	return adapter;
 }
@@ -525,6 +530,31 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
 		bearer_added(proxy);
 	} else if (!strcmp(interface, "org.bluez.Bearer.LE1")) {
 		bearer_added(proxy);
+	} else if (!strcmp(interface, "org.bluez.ChannelSounding1")) {
+		const char *cs_path = g_dbus_proxy_get_path(proxy);
+		GList *list;
+
+		for (list = g_list_first(ctrl_list); list;
+		     list = g_list_next(list)) {
+			struct adapter *a = list->data;
+			const char *ap;
+			size_t ap_len;
+
+			if (!a->proxy)
+				continue;
+
+			ap = g_dbus_proxy_get_path(a->proxy);
+			ap_len = strlen(ap);
+
+			if (!strncmp(cs_path, ap, ap_len) &&
+						cs_path[ap_len] == '/') {
+				a->cs_proxies = g_list_append(
+							a->cs_proxies, proxy);
+				break;
+			}
+		}
+
+		cs_proxy_added(proxy);
 	}
 }
 
@@ -577,6 +607,7 @@ static void adapter_removed(GDBusProxy *proxy)
 			g_list_free(adapter->devices);
 			g_list_free(adapter->sets);
 			g_list_free(adapter->bearers);
+			g_list_free(adapter->cs_proxies);
 			g_free(adapter);
 			g_list_free(ll);
 			return;
@@ -656,6 +687,30 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
 		bearer_removed(proxy);
 	} else if (!strcmp(interface, "org.bluez.Bearer.LE1")) {
 		bearer_removed(proxy);
+	} else if (!strcmp(interface, "org.bluez.ChannelSounding1")) {
+		const char *cs_path = g_dbus_proxy_get_path(proxy);
+		GList *list;
+
+		for (list = g_list_first(ctrl_list); list;
+		     list = g_list_next(list)) {
+			struct adapter *a = list->data;
+			const char *ap;
+			size_t ap_len;
+
+			if (!a->proxy)
+				continue;
+
+			ap = g_dbus_proxy_get_path(a->proxy);
+			ap_len = strlen(ap);
+
+			if (!strncmp(cs_path, ap, ap_len) &&
+						cs_path[ap_len] == '/') {
+				a->cs_proxies = g_list_remove(
+							a->cs_proxies, proxy);
+				break;
+			}
+		}
+		cs_proxy_removed(proxy);
 	}
 }
 
@@ -729,6 +784,10 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 				else if (!connected && default_dev == proxy)
 					set_default_device(NULL, NULL);
 
+				if (!connected)
+					cs_device_disconnected(
+						g_dbus_proxy_get_path(proxy));
+
 				/* If the device is connected and the filter
 				 * is auto_connect and it matches the pattern,
 				 * stop discovery.
@@ -821,6 +880,16 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 			print_iter(str, name, iter);
 			g_free(str);
 		}
+	} else if (!strcmp(interface, "org.bluez.ChannelSounding1")) {
+		if (!strcmp(name, "Active")) {
+			dbus_bool_t active;
+
+			dbus_message_iter_get_basic(iter, &active);
+			if (active)
+				cs_measurement_started(proxy);
+			else
+				cs_measurement_stopped(proxy);
+		}
 	}
 }
 
@@ -1090,6 +1159,7 @@ static void cmd_select(int argc, char *argv[])
 		return bt_shell_noninteractive_quit(EXIT_SUCCESS);
 
 	default_ctrl = adapter;
+	cs_set_device_list(&adapter->devices);
 	print_adapter(adapter->proxy, NULL);
 
 	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
@@ -4003,6 +4073,7 @@ int main(int argc, char *argv[])
 	assistant_add_submenu();
 	hci_add_submenu();
 	telephony_add_submenu();
+	cs_add_submenu();
 	bt_shell_set_prompt(PROMPT_OFF, NULL);
 
 	bt_shell_handle_non_interactive_help();
-- 


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

* RE: Add D-Bus and bluetoothctl support for Channel Sounding control
  2026-07-06 16:15 ` [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs Naga Bhavani Akella
@ 2026-07-06 17:05   ` bluez.test.bot
  0 siblings, 0 replies; 12+ messages in thread
From: bluez.test.bot @ 2026-07-06 17:05 UTC (permalink / raw)
  To: linux-bluetooth, naga.akella

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

---Test result---

Test Summary:
CheckPatch                    FAIL      3.48 seconds
GitLint                       FAIL      1.67 seconds
BuildEll                      PASS      20.20 seconds
BluezMake                     FAIL      514.60 seconds
MakeCheck                     FAIL      25.64 seconds
MakeDistcheck                 PASS      151.59 seconds
CheckValgrind                 FAIL      131.61 seconds
CheckSmatch                   FAIL      284.76 seconds
bluezmakeextell               FAIL      89.26 seconds
IncrementalBuild              FAIL      535.76 seconds
ScanBuild                     FAIL      414.05 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v1,5/5] client: Add Channel Sounding shell submenu
ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#218: FILE: client/cs.c:25:
+static GList *cs_proxies;
              ^

/github/workspace/src/patch/14667742.patch total: 1 errors, 0 warnings, 1148 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/14667742.patch has style problems, please review.

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

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


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v1,3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples

1: T1 Title exceeds max length (81>80): "[BlueZ,v1,3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples"
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12990:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12990 | int main(int argc, char *argv[])
      |     ^~~~
unit/test-avdtp.c: In function ‘main’:
unit/test-avdtp.c:766:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
  766 | int main(int argc, char *argv[])
      |     ^~~~
unit/test-avrcp.c: In function ‘main’:
unit/test-avrcp.c:989:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
  989 | int main(int argc, char *argv[])
      |     ^~~~
profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8999: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4175: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:

profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8999: profiles/ranging/bluetoothd-rap.o] Error 1
make: *** [Makefile:10826: check] Error 2
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:

tools/mgmt-tester.c: In function ‘main’:
tools/mgmt-tester.c:12990:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
12990 | int main(int argc, char *argv[])
      |     ^~~~
profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8999: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:10826: 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:271:25: warning: Variable length array is used.
src/shared/gatt-server.c:614:25: warning: Variable length array is used.
src/shared/gatt-server.c:712:25: warning: Variable length array is used.
src/shared/bap.c:317: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/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/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:271:25: warning: Variable length array is used.
src/shared/gatt-server.c:614:25: warning: Variable length array is used.
src/shared/gatt-server.c:712:25: warning: Variable length array is used.
src/shared/bap.c:317: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/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'
tools/mesh-cfgtest.c:1453:17: warning: unknown escape sequence: '\%'
tools/sco-tester.c: note: in included file:
./lib/bluetooth/bluetooth.h:232:15: warning: array of flexible structures
./lib/bluetooth/bluetooth.h:237:31: warning: array of flexible structures
tools/bneptest.c:634:39: warning: unknown escape sequence: '\%'
tools/seq2bseq.c:57:26: warning: Variable length array is used.
tools/obex-client-tool.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'
client/btpclient/gatt.c: note: in included file:
./src/shared/btp.h:335:41: warning: array of flexible structures
./src/shared/btp.h:340:55: warning: array of flexible structures
./src/shared/btp.h:363:47: warning: array of flexible structures
./src/shared/btp.h:392:42: warning: array of flexible structures
src/advertising.c: note: in included file:
./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability
src/agent.c: note: in included file:
src/shared/queue.h:19:20: error: redefinition of struct queue_entry
src/adv_monitor.c: note: in included file:
./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability
unit/avctp.c:505:34: warning: Variable length array is used.
unit/avctp.c:556:34: warning: Variable length array is used.
unit/test-avrcp.c:373:26: warning: Variable length array is used.
unit/test-avrcp.c:398:26: warning: Variable length array is used.
unit/test-avrcp.c:414:24: warning: Variable length array is used.
unit/avrcp-lib.c:1085:34: warning: Variable length array is used.
unit/avrcp-lib.c:1583:34: warning: Variable length array is used.
unit/avrcp-lib.c:1612:34: warning: Variable length array is used.
unit/avrcp-lib.c:1638:34: warning: Variable length array is used.
src/advertising.c: note: in included file:
./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability
src/agent.c: note: in included file:
src/shared/queue.h:19:20: error: redefinition of struct queue_entry
src/adv_monitor.c: note: in included file:
./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability
src/main.c: note: in included file (through src/device.h):
./src/shared/queue.h:19:20: error: redefinition of struct queue_entry
mesh/mesh-io-mgmt.c:525:67: warning: Variable length array is used.
client/display.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/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-server.c:271:25: 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:614:25: warning: Variable length array is used.
src/shared/gatt-server.c:712:25: warning: Variable length array is used.
src/shared/bap.c:317: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/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'
monitor/packet.c:2002:26: warning: Variable length array is used.
monitor/packet.c: note: in included file:
monitor/bt.h:3924:52: warning: array of flexible structures
monitor/bt.h:3912:40: warning: array of flexible structures
monitor/msft.c: note: in included file:
monitor/msft.h:88:44: warning: array of flexible structures
tools/rctest.c:631:33: warning: non-ANSI function declaration of function 'automated_send_recv'
tools/hex2hcd.c:136:26: warning: Variable length array is used.
tools/meshctl.c:324:33: warning: non-ANSI function declaration of function 'forget_mesh_devices'
tools/mesh-gatt/node.c:456:39: warning: non-ANSI function declaration of function 'node_get_local_node'
tools/mesh-gatt/net.c:1239:30: warning: non-ANSI function declaration of function 'get_next_seq'
tools/mesh-gatt/net.c:2193:29: warning: non-ANSI function declaration of function 'net_get_default_ttl'
tools/mesh-gatt/net.c:2207:26: warning: non-ANSI function declaration of function 'net_get_seq_num'
tools/mesh-gatt/prov.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'
tools/mesh-gatt/onoff-model.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'
ell/log.c:431:65: warning: non-ANSI function declaration of function 'register_debug_section'
ell/log.c:439:68: warning: non-ANSI function declaration of function 'free_debug_sections'
ell/random.c:60:42: warning: non-ANSI function declaration of function 'l_getrandom_is_supported'
ell/cipher.c:660:28: warning: non-ANSI function declaration of function 'init_supported'
ell/checksum.c:382:28: warning: non-ANSI function declaration of function 'init_supported'
ell/checksum.c:444:47: warning: non-ANSI function declaration of function 'l_checksum_cmac_aes_supported'
ell/cipher.c:519:24: warning: Variable length array is used.
ell/cert-crypto.c:36:33: warning: Variable length array is used.
ell/cert-crypto.c:142:36: warning: Variable length array is used.
ell/cert-crypto.c:198:36: warning: Variable length array is used.
ell/cert-crypto.c:251:31: warning: Variable length array is used.
ell/key.c:550:25: warning: Variable length array is used.
ell/dbus-service.c:548:49: warning: non-ANSI function declaration of function '_dbus_object_tree_new'
ell/dbus-filter.c:233:46: warning: Variable length array is used.
ell/tls.c:45:25: warning: Variable length array is used.
ell/tls.c:86:22: warning: Variable length array is used.
ell/tls.c:86:46: warning: Variable length array is used.
ell/tls.c:1819:26: warning: Variable length array is used.
ell/tls-suites.c:1079:25: warning: Variable length array is used.
ell/tls-suites.c:1081:34: warning: Variable length array is used.
ell/tls-suites.c:1084:41: warning: Variable length array is used.
ell/tls-suites.c:1133:41: warning: Variable length array is used.
emulator/btdev.c:478:29: warning: Variable length array is used.
emulator/bthost.c:703:28: warning: Variable length array is used.
emulator/bthost.c:704:32: warning: Variable length array is used.
emulator/bthost.c:944:28: warning: Variable length array is used.
emulator/bthost.c:978:28: warning: Variable length array is used.
emulator/bthost.c:979:32: warning: Variable length array is used.
attrib/gatttool.c:236:23: warning: Variable length array is used.
attrib/interactive.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'
attrib/interactive.c:175:27: warning: non-ANSI function declaration of function 'disconnect_io'
attrib/interactive.c:300:23: warning: Variable length array is used.
profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8999: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4175: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:

profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8999: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4175: all] Error 2
##############################
Test: IncrementalBuild - FAIL
Desc: Incremental build with the patches in the series
Output:

profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8994: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4173: all] Error 2
[BlueZ,v1,4/5] profiles: Add D-Bus Channel Sounding control API

profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8994: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4173: 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/bap.c:1542:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2353:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
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.
tools/hciattach.c:817:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
        if ((n = read_hci_event(fd, resp, 10)) < 0) {
             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:865:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
        if ((n = read_hci_event(fd, resp, 4)) < 0) {
             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:887:8: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
                if ((n = read_hci_event(fd, resp, 10)) < 0) {
                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:909:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
        if ((n = read_hci_event(fd, resp, 4)) < 0) {
             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:930:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
        if ((n = read_hci_event(fd, resp, 4)) < 0) {
             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:974:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
        if ((n = read_hci_event(fd, resp, 6)) < 0) {
             ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 warnings generated.
src/shared/bap.c:1542:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2353:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/oui.c:50:2: warning: Value stored to 'hwdb' is never read
        hwdb = udev_hwdb_unref(hwdb);
        ^      ~~~~~~~~~~~~~~~~~~~~~
src/oui.c:53:2: warning: Value stored to 'udev' is never read
        udev = udev_unref(udev);
        ^      ~~~~~~~~~~~~~~~~
2 warnings generated.
tools/rfcomm.c:234:3: warning: Value stored to 'i' is never read
                i = execvp(cmdargv[0], cmdargv);
                ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:234:7: warning: Null pointer passed to 1st parameter expecting 'nonnull'
                i = execvp(cmdargv[0], cmdargv);
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:354:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
                if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
                     ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:497:14: warning: Assigned value is garbage or undefined
        req.channel = raddr.rc_channel;
                    ^ ~~~~~~~~~~~~~~~~
tools/rfcomm.c:515:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
                if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
                     ^    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 warnings generated.
tools/ciptool.c:351:7: warning: 5th function call argument is an uninitialized value
        sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
                buf[1] = data[i + 1];
                       ^ ~~~~~~~~~~~
src/sdp-xml.c:306:11: warning: Assigned value is garbage or undefined
                        buf[1] = data[i + 1];
                               ^ ~~~~~~~~~~~
src/sdp-xml.c:344:11: warning: Assigned value is garbage or undefined
                        buf[1] = data[i + 1];
                               ^ ~~~~~~~~~~~
3 warnings generated.
tools/sdptool.c:941:26: warning: Result of 'malloc' is converted to a pointer of type 'uint32_t', which is incompatible with sizeof operand type 'int'
                        uint32_t *value_int = malloc(sizeof(int));
                        ~~~~~~~~~~            ^~~~~~ ~~~~~~~~~~~
tools/sdptool.c:980:4: warning: 1st function call argument is an uninitialized value
                        free(allocArray[i]);
                        ^~~~~~~~~~~~~~~~~~~
tools/sdptool.c:3777:2: warning: Potential leak of memory pointed to by 'si.name'
        return add_service(0, &si);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~
tools/sdptool.c:4112:4: warning: Potential leak of memory pointed to by 'context.svc'
                        return -1;
                        ^~~~~~~~~
4 warnings generated.
tools/avtest.c:243:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 3);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:253:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 4);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:262:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 3);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:276:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf,
                                ^     ~~~~~~~~~~~~~~
tools/avtest.c:283:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf,
                                ^     ~~~~~~~~~~~~~~
tools/avtest.c:290:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf,
                                ^     ~~~~~~~~~~~~~~
tools/avtest.c:297:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf,
                                ^     ~~~~~~~~~~~~~~
tools/avtest.c:309:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 4);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:313:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:322:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 3);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:326:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:335:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 3);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:342:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:364:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 4);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:368:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:377:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 3);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:381:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:394:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 4);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:398:5: warning: Value stored to 'len' is never read
                                len = write(sk, buf, 2);
                                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:405:4: warning: Value stored to 'len' is never read
                        len = write(sk, buf, 2);
                        ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:415:4: warning: Value stored to 'len' is never read
                        len = write(sk, buf, 2);
                        ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:580:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 2);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:588:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, invalid ? 2 : 3);
                ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:602:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 4 + media_transport_size);
                ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:615:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:625:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:637:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:652:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:664:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:673:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 3);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:680:3: warning: Value stored to 'len' is never read
                len = write(sk, buf, 2);
                ^     ~~~~~~~~~~~~~~~~~
tools/avtest.c:716:2: warning: Value stored to 'len' is never read
        len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
        ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 warnings generated.
tools/btproxy.c:836:15: warning: Null pointer passed to 1st parameter expecting 'nonnull'
                        tcp_port = atoi(optarg);
                                   ^~~~~~~~~~~~
tools/btproxy.c:839:8: warning: Null pointer passed to 1st parameter expecting 'nonnull'
                        if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
                            ^~~~~~~~~~~~~~
2 warnings generated.
tools/create-image.c:76:3: warning: Value stored to 'fd' is never read
                fd = -1;
                ^    ~~
tools/create-image.c:84:3: warning: Value stored to 'fd' is never read
                fd = -1;
                ^    ~~
tools/create-image.c:92:3: warning: Value stored to 'fd' is never read
                fd = -1;
                ^    ~~
tools/create-image.c:105:2: warning: Value stored to 'fd' is never read
        fd = -1;
        ^    ~~
4 warnings generated.
tools/check-selftest.c:42:3: warning: Value stored to 'ptr' is never read
                ptr = fgets(result, sizeof(result), fp);
                ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/btgatt-client.c:1822:2: warning: Value stored to 'argv' is never read
        argv += optind;
        ^       ~~~~~~
1 warning generated.
tools/btgatt-server.c:1204:2: warning: Value stored to 'argv' is never read
        argv -= optind;
        ^       ~~~~~~
1 warning generated.
tools/gatt-service.c:294:2: warning: 2nd function call argument is an uninitialized value
        chr_write(chr, value, len);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/obex-server-tool.c:133:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
        data->fd = open(name, O_WRONLY | O_CREAT | O_NOCTTY, 0600);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/obex-server-tool.c:192:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
        data->fd = open(name, O_RDONLY | O_NOCTTY, 0);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
tools/test-runner.c:1370:2: warning: Address of stack memory associated with local variable 'kernel_path' is still referred to by the global variable 'kernel_image' upon returning to the caller.  This will be a dangling reference
        return EXIT_SUCCESS;
        ^~~~~~~~~~~~~~~~~~~
1 warning generated.
client/btpclient/btpclientctl.c:402:3: warning: Value stored to 'bit' is never read
                bit = 0;
                ^     ~
client/btpclient/btpclientctl.c:1655:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
        memcpy(cp->data, ad_data, ad_len);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdp-client.c:353:14: warning: Access to field 'cb' results in a dereference of a null pointer
        (*ctxt)->cb = cb;
        ~~~~~~~~~~~~^~~~
1 warning generated.
src/sdpd-request.c:209:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
                                pElem = malloc(sizeof(uint16_t));
                                        ^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:237:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
                                pElem = malloc(sizeof(uint32_t));
                                        ^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
src/gatt-database.c:1171:10: warning: Value stored to 'bits' during its initialization is never read
        uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
                ^~~~     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/gatt-client.c:1569:2: warning: Use of memory after it is freed
        notify_client_unref(client);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
unit/avrcp-lib.c:1968:3: warning: 1st function call argument is an uninitialized value
                g_free(text[i]);
                ^~~~~~~~~~~~~~~
1 warning generated.
unit/avdtp.c:756:25: warning: Use of memory after it is freed
                session->prio_queue = g_slist_remove(session->prio_queue, req);
                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
unit/avdtp.c:763:24: warning: Use of memory after it is freed
                session->req_queue = g_slist_remove(session->req_queue, req);
                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
unit/test-util.c:33:8: warning: Potential leak of memory pointed to by 'p1'
        p2[0] = 1;
        ~~~~~~^~~
unit/test-util.c:36:3: warning: Potential leak of memory pointed to by 'p2'
                _cleanup_free_ uint8_t *data = NULL;
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
./src/shared/util.h:134:24: note: expanded from macro '_cleanup_free_'
#define _cleanup_free_ _cleanup_(freep)
                       ^
./src/shared/util.h:132:22: note: expanded from macro '_cleanup_'
#define _cleanup_(f) __attribute__((cleanup(f)))
                     ^
unit/test-util.c:42:3: warning: Potential leak of memory pointed to by 'data'
                assert(is_null_too == NULL);
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/assert.h:108:11: note: expanded from macro 'assert'
  ((void) sizeof ((expr) ? 1 : 0), __extension__ ({                     \
          ^~~~~~~~~~~~~~~~~~~~~~~
unit/test-util.c:50:2: warning: Potential leak of memory pointed to by 'data'
        assert(is_null == NULL);
        ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/assert.h:108:11: note: expanded from macro 'assert'
  ((void) sizeof ((expr) ? 1 : 0), __extension__ ({                     \
          ^~~~~~~~~~~~~~~~~~~~~~~
4 warnings generated.
profiles/audio/avdtp.c:895:25: warning: Use of memory after it is freed
                session->prio_queue = g_slist_remove(session->prio_queue, req);
                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
profiles/audio/avdtp.c:902:24: warning: Use of memory after it is freed
                session->req_queue = g_slist_remove(session->req_queue, req);
                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
profiles/audio/a2dp.c:442:8: warning: Use of memory after it is freed
                if (!cb->resume_cb)
                     ^~~~~~~~~~~~~
profiles/audio/a2dp.c:3354:20: warning: Access to field 'starting' results in a dereference of a null pointer (loaded from variable 'stream')
                stream->starting = TRUE;
                ~~~~~~           ^
profiles/audio/a2dp.c:3357:8: warning: Access to field 'suspending' results in a dereference of a null pointer (loaded from variable 'stream')
                if (!stream->suspending && stream->suspend_timer) {
                     ^~~~~~~~~~~~~~~~~~
profiles/audio/a2dp.c:3417:22: warning: Access to field 'suspending' results in a dereference of a null pointer (loaded from variable 'stream')
                stream->suspending = TRUE;
                ~~~~~~             ^
4 warnings generated.
profiles/audio/avrcp.c:1968:2: warning: Value stored to 'operands' is never read
        operands += sizeof(*pdu);
        ^           ~~~~~~~~~~~~
1 warning generated.
attrib/gatt.c:970:2: warning: Potential leak of memory pointed to by 'long_write'
        return prepare_write(long_write);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdpd-request.c:209:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
                                pElem = malloc(sizeof(uint16_t));
                                        ^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:237:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
                                pElem = malloc(sizeof(uint32_t));
                                        ^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdp-client.c:353:14: warning: Access to field 'cb' results in a dereference of a null pointer
        (*ctxt)->cb = cb;
        ~~~~~~~~~~~~^~~~
1 warning generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
                buf[1] = data[i + 1];
                       ^ ~~~~~~~~~~~
src/sdp-xml.c:306:11: warning: Assigned value is garbage or undefined
                        buf[1] = data[i + 1];
                               ^ ~~~~~~~~~~~
src/sdp-xml.c:344:11: warning: Assigned value is garbage or undefined
                        buf[1] = data[i + 1];
                               ^ ~~~~~~~~~~~
3 warnings generated.
src/gatt-database.c:1171:10: warning: Value stored to 'bits' during its initialization is never read
        uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
                ^~~~     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/gatt-client.c:1569:2: warning: Use of memory after it is freed
        notify_client_unref(client);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-header.c:95:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
        memcpy(to, from, count);
        ^~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-transfer.c:423:7: warning: Use of memory after it is freed
        if (!g_slist_find(transfers, transfer))
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
mesh/main.c:162:3: warning: Value stored to 'optarg' is never read
                optarg += strlen("auto");
                ^         ~~~~~~~~~~~~~~
1 warning generated.
lib/bluetooth/hci.c:93:4: warning: Value stored to 'ptr' is never read
                        ptr += sprintf(ptr, "%s", m->str);
                        ^      ~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
client/player.c:2363:8: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
                if (!strcmp(ep->path, pattern))
                     ^~~~~~~~~~~~~~~~~~~~~~~~~
client/player.c:3640:16: warning: Null pointer passed to 1st parameter expecting 'nonnull'
        codec->name = strdup(name);
                      ^~~~~~~~~~~~
2 warnings generated.
gdbus/watch.c:226:3: warning: Attempt to free released memory
                g_free(l->data);
                ^~~~~~~~~~~~~~~
1 warning generated.
lib/bluetooth/sdp.c:509:17: warning: Dereference of undefined pointer value
                uint8_t dtd = *(uint8_t *) dtds[i];
                              ^~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:539:17: warning: Dereference of undefined pointer value
                uint8_t dtd = *(uint8_t *) dtds[i];
                              ^~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:1891:26: warning: Potential leak of memory pointed to by 'ap'
        for (; pdlist; pdlist = pdlist->next) {
                                ^~~~~~
lib/bluetooth/sdp.c:1905:6: warning: Potential leak of memory pointed to by 'pds'
                ap = sdp_list_append(ap, pds);
                ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:1950:10: warning: Potential leak of memory pointed to by 'u'
                        *seqp = sdp_list_append(*seqp, u);
                        ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:2055:4: warning: Potential leak of memory pointed to by 'lang'
                        sdp_list_free(*langSeq, free);
                        ^~~~~~~~~~~~~
lib/bluetooth/sdp.c:2144:9: warning: Potential leak of memory pointed to by 'profDesc'
        return 0;
               ^
lib/bluetooth/sdp.c:3276:8: warning: Potential leak of memory pointed to by 'pSvcRec'
                pSeq = sdp_list_append(pSeq, pSvcRec);
                ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:3277:9: warning: Potential leak of memory pointed to by 'pSeq'
                pdata += sizeof(uint32_t);
                ~~~~~~^~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:4613:13: warning: Potential leak of memory pointed to by 'rec_list'
                        } while (scanned < attr_list_len && pdata_len > 0);
                                 ^~~~~~~
lib/bluetooth/sdp.c:4909:40: warning: Potential leak of memory pointed to by 'tseq'
        for (d = sdpdata->val.dataseq; d; d = d->next) {
                                              ^
lib/bluetooth/sdp.c:4945:8: warning: Potential leak of memory pointed to by 'subseq'
                tseq = sdp_list_append(tseq, subseq);
                ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 warnings generated.
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/bap.c:1542:8: warning: Use of memory after it is freed
        bap = bt_bap_ref_safe(bap);
              ^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2353:20: warning: Use of memory after it is freed
        return queue_find(stream->bap->streams, NULL, stream);
                          ^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
monitor/hwdb.c:59:2: warning: Value stored to 'hwdb' is never read
        hwdb = udev_hwdb_unref(hwdb);
        ^      ~~~~~~~~~~~~~~~~~~~~~
monitor/hwdb.c:64:2: warning: Value stored to 'udev' is never read
        udev = udev_unref(udev);
        ^      ~~~~~~~~~~~~~~~~
monitor/hwdb.c:106:2: warning: Value stored to 'hwdb' is never read
        hwdb = udev_hwdb_unref(hwdb);
        ^      ~~~~~~~~~~~~~~~~~~~~~
monitor/hwdb.c:111:2: warning: Value stored to 'udev' is never read
        udev = udev_unref(udev);
        ^      ~~~~~~~~~~~~~~~~
4 warnings generated.
monitor/l2cap.c:1676:4: warning: Value stored to 'data' is never read
                        data += len;
                        ^       ~~~
monitor/l2cap.c:1677:4: warning: Value stored to 'size' is never read
                        size -= len;
                        ^       ~~~
2 warnings generated.
tools/bluemoon.c:1102:8: warning: Null pointer passed to 1st parameter expecting 'nonnull'
                        if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
                            ^~~~~~~~~~~~~~
1 warning generated.
tools/meshctl.c:326:19: warning: Access to field 'mesh_devices' results in a dereference of a null pointer (loaded from variable 'default_ctrl')
        g_list_free_full(default_ctrl->mesh_devices, g_free);
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~
tools/meshctl.c:762:2: warning: 2nd function call argument is an uninitialized value
        bt_shell_printf("Attempting to disconnect from %s\n", addr);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/meshctl.c:1957:2: warning: Value stored to 'len' is never read
        len = len + extra + strlen("local_node.json");
        ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
In file included from tools/mesh-gatt/crypto.c:32:
./src/shared/util.h:291:9: warning: 1st function call argument is an uninitialized value
        return be32_to_cpu(get_unaligned((const uint32_t *) ptr));
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./src/shared/util.h:41:26: note: expanded from macro 'be32_to_cpu'
#define be32_to_cpu(val) bswap_32(val)
                         ^~~~~~~~~~~~~
/usr/include/byteswap.h:34:21: note: expanded from macro 'bswap_32'
#define bswap_32(x) __bswap_32 (x)
                    ^~~~~~~~~~~~~~
In file included from tools/mesh-gatt/crypto.c:32:
./src/shared/util.h:301:9: warning: 1st function call argument is an uninitialized value
        return be64_to_cpu(get_unaligned((const uint64_t *) ptr));
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./src/shared/util.h:42:26: note: expanded from macro 'be64_to_cpu'
#define be64_to_cpu(val) bswap_64(val)
                         ^~~~~~~~~~~~~
/usr/include/byteswap.h:37:21: note: expanded from macro 'bswap_64'
#define bswap_64(x) __bswap_64 (x)
                    ^~~~~~~~~~~~~~
2 warnings generated.
ell/util.c:853:8: warning: The left operand of '>' is a garbage value
        if (x > UINT8_MAX)
            ~ ^
ell/util.c:871:8: warning: The left operand of '>' is a garbage value
        if (x > UINT16_MAX)
            ~ ^
2 warnings generated.
ell/pem.c:131:8: warning: Dereference of null pointer (loaded from variable 'eol')
                        if (*eol == '\r' || *eol == '\n')
                            ^~~~
ell/pem.c:166:18: warning: Dereference of null pointer (loaded from variable 'eol')
                if (buf_len && *eol == '\r' && *buf_ptr == '\n') {
                               ^~~~
ell/pem.c:166:34: warning: Dereference of null pointer (loaded from variable 'buf_ptr')
                if (buf_len && *eol == '\r' && *buf_ptr == '\n') {
                                               ^~~~~~~~
ell/pem.c:304:11: warning: 1st function call argument is an uninitialized value
        result = pem_load_buffer(file.data, file.st.st_size,
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ell/pem.c:469:9: warning: 1st function call argument is an uninitialized value
        list = l_pem_load_certificate_list_from_data(file.data,
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 warnings generated.
ell/cert.c:645:41: warning: Access to field 'asn1_len' results in a dereference of a null pointer (loaded from variable 'cert')
        key = l_key_new(L_KEY_RSA, cert->asn1, cert->asn1_len);
                                               ^~~~~~~~~~~~~~
1 warning generated.
ell/gvariant-util.c:143:18: warning: The left operand of '>' is a garbage value
                        if (alignment > max_alignment)
                            ~~~~~~~~~ ^
ell/gvariant-util.c:456:5: warning: Dereference of null pointer
                        !children[0].fixed_size) {
                         ^~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
emulator/serial.c:150:2: warning: Assigned value is garbage or undefined
        enum btdev_type uninitialized_var(type);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/serial.c:150:36: warning: Value stored to 'type' during its initialization is never read
        enum btdev_type uninitialized_var(type);
                                          ^~~~
emulator/serial.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
                             ^   ~
emulator/serial.c:213:2: warning: Assigned value is garbage or undefined
        enum btdev_type uninitialized_var(dev_type);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/serial.c:213:36: warning: Value stored to 'dev_type' during its initialization is never read
        enum btdev_type uninitialized_var(dev_type);
                                          ^~~~~~~~
emulator/serial.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
                             ^   ~
4 warnings generated.
ell/ecc-external.c:77:11: warning: Assigned value is garbage or undefined
                dest[i] = src[i];
                        ^ ~~~~~~
ell/ecc-external.c:160:18: warning: The right operand of '-' is a garbage value
                diff = left[i] - right[i] - borrow;
                               ^ ~~~~~~~~
ell/ecc-external.c:227:14: warning: 2nd function call argument is an uninitialized value
                        product = mul_64_64(left[i], right[k - i]);
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ell/ecc-external.c:408:9: warning: Assigned value is garbage or undefined
        tmp[1] = product[3];
               ^ ~~~~~~~~~~
ell/ecc-external.c:435:22: warning: The left operand of '&' is a garbage value
        tmp[1] = product[3] & 0xffffffff00000000ull;
                 ~~~~~~~~~~ ^
ell/ecc-external.c:483:22: warning: The left operand of '&' is a garbage value
        tmp[1] = product[5] & 0xffffffff00000000ull;
                 ~~~~~~~~~~ ^
ell/ecc-external.c:688:28: warning: The left operand of '>>' is a garbage value
                tmp[i] = (product[8 + i] >> 9) | (product[9 + i] << 55);
                          ~~~~~~~~~~~~~~ ^
7 warnings generated.
emulator/server.c:230:2: warning: Assigned value is garbage or undefined
        enum btdev_type uninitialized_var(type);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/server.c:230:36: warning: Value stored to 'type' during its initialization is never read
        enum btdev_type uninitialized_var(type);
                                          ^~~~
emulator/server.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
                             ^   ~
2 warnings generated.
emulator/b1ee.c:258:3: warning: Potential leak of memory pointed to by 'server_port'
                int opt;
                ^~~~~~~
emulator/b1ee.c:258:3: warning: Potential leak of memory pointed to by 'sniffer_port'
                int opt;
                ^~~~~~~
emulator/b1ee.c:289:2: warning: Value stored to 'argc' is never read
        argc = argc - optind;
        ^      ~~~~~~~~~~~~~
3 warnings generated.
gobex/gobex-header.c:95:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
        memcpy(to, from, count);
        ^~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-transfer.c:423:7: warning: Use of memory after it is freed
        if (!g_slist_find(transfers, transfer))
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
attrib/gatt.c:970:2: warning: Potential leak of memory pointed to by 'long_write'
        return prepare_write(long_write);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
emulator/btdev.c:6662:20: warning: Access to field 'link' results in a dereference of a null pointer (loaded from variable 'acl')
                le_past_received(acl->link, pa);
                                 ^~~~~~~~~
emulator/btdev.c:6762:25: warning: Access to field 'link' results in a dereference of a null pointer (loaded from variable 'acl')
                le_past_info_received(acl->link, ea);
                                      ^~~~~~~~~
2 warnings generated.
profiles/ranging/rap.c:132:1: error: ‘rap_adapter_data_ref’ defined but not used [-Werror=unused-function]
  132 | rap_adapter_data_ref(struct btd_adapter *adapter)
      | ^~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[1]: *** [Makefile:8994: profiles/ranging/bluetoothd-rap.o] Error 1
make[1]: *** Waiting for unfinished jobs....
profiles/audio/media.c:1117:7: warning: Use of memory after it is freed
                if (req->cb != pac_select_cb) {
                    ^~~~~~~
1 warning generated.
make: *** [Makefile:4173: all] Error 2


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

---
Regards,
Linux Bluetooth


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

* Re: [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-06 16:16 ` [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples Naga Bhavani Akella
@ 2026-07-06 20:36   ` Luiz Augusto von Dentz
  2026-07-07  7:34     ` Naga Bhavani Akella
  0 siblings, 1 reply; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-06 20:36 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg

Hi Naga,

On Mon, Jul 6, 2026 at 12:16 PM Naga Bhavani Akella
<naga.akella@oss.qualcomm.com> wrote:
>
> Add :Used by: field linking to bluetoothctl cs submenu and
> Examples section showing corresponding bluetoothctl cs commands
> for D-Bus methods
> ---
>  doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
>  doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++

These 2 needs to be splitted, each file on its own commit.

>  2 files changed, 580 insertions(+)
>  create mode 100644 doc/bluetoothctl-cs.rst
>  create mode 100644 doc/org.bluez.ChannelSounding1.rst
>
> diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
> new file mode 100644
> index 000000000..c16c89298
> --- /dev/null
> +++ b/doc/bluetoothctl-cs.rst
> @@ -0,0 +1,278 @@
> +================
> +bluetoothctl-cs
> +================
> +
> +--------------------------
> +Channel Sounding Submenu
> +--------------------------
> +
> +:Version: BlueZ
> +:Copyright: Free use of this software is granted under the terms of the GNU
> +            Lesser General Public Licenses (LGPL).
> +:Date: June 2026
> +:Manual section: 1
> +:Manual group: Linux System Administration
> +
> +SYNOPSIS
> +========
> +
> +**bluetoothctl** [--options] [cs.commands]
> +
> +This submenu controls Bluetooth Channel Sounding (CS) distance measurement
> +using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
> +starting and stopping measurements and inspecting the current parameter
> +state and active session identifier.
> +
> +CS parameters can be overridden on any **start** call using inline
> +``param=value`` arguments. Overrides are applied to the local parameter
> +state before the measurement is started, so **show** reflects them
> +immediately after.
> +
> +
> +Channel Sounding Commands
> +=========================
> +
> +start
> +-----
> +
> +Sets one or more CS parameters and starts a distance measurement on the
> +connected device. All configuration is sent to the daemon in a single
> +**StartMeasurement** call. On success the device path is printed to
> +the console. Multiple simultaneous sessions across different devices are
> +supported; each is tracked independently.
> +
> +Calling **start** on a device that already has an active measurement
> +returns an error without starting a second session on the same device.
> +
> +Positional arguments are optional and must appear before any
> +``param=value`` pairs:
> +
> +- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
> +  no timeout.
> +
> +Any additional argument of the form ``param=value`` overrides the named
> +parameter for this call and for all subsequent calls. Array-valued
> +parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
> +use colon-separated hex bytes with no ``0x`` prefix.
> +
> +:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
> +:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
> +:[dev_addr]: Bluetooth address of the target device (optional; uses the
> +             only available CS-capable device when omitted)
> +:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
> +:[param=value]: One or more ``param=value`` overrides (optional)
> +
> +**Settable parameters:**
> +
> +.. list-table::
> +   :header-rows: 1
> +   :widths: 35 15 50
> +
> +   * - Parameter
> +     - Default
> +     - Description
> +   * - ``role``
> +     - ``0x03``
> +     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
> +   * - ``cs_sync_ant_sel``
> +     - ``0xFF``
> +     - CS sync antenna selection (0xFE/0xFF reserved)
> +   * - ``max_tx_power``
> +     - ``20``
> +     - Max TX power in dBm (signed, range −127 to +20)
> +   * - ``config_id``
> +     - ``0``
> +     - CS configuration identifier
> +   * - ``main_mode_type``
> +     - ``1``
> +     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
> +   * - ``sub_mode_type``
> +     - ``0xFF``
> +     - Sub-mode within main mode; ``0xFF`` = unused
> +   * - ``main_mode_min_steps``
> +     - ``2``
> +     - Min CS main mode steps per subevent
> +   * - ``main_mode_max_steps``
> +     - ``3``
> +     - Max CS main mode steps per subevent
> +   * - ``main_mode_repetition``
> +     - ``1``
> +     - Times main mode steps are repeated in a subevent
> +   * - ``mode0_steps``
> +     - ``2``
> +     - CS Mode 0 steps at the beginning of each subevent
> +   * - ``rtt_types``
> +     - ``0``
> +     - RTT measurement types bitmask
> +   * - ``cs_sync_phy``
> +     - ``0x01``
> +     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
> +   * - ``channel_map``
> +     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
> +     - 10-byte channel map bitmap (colon-separated hex)
> +   * - ``channel_map_repetition``
> +     - ``1``
> +     - Consecutive repetitions of the channel map
> +   * - ``channel_selection_type``
> +     - ``0``
> +     - CS channel selection algorithm
> +   * - ``channel_shape``
> +     - ``0``
> +     - Shape used in channel selection algorithm
> +   * - ``channel_jump``
> +     - ``2``
> +     - Channel jump size
> +   * - ``companion_signal_enable``
> +     - ``0``
> +     - ``1`` to transmit companion signal, ``0`` to disable
> +   * - ``max_procedure_duration``
> +     - ``1600``
> +     - Maximum duration of one CS measurement procedure
> +   * - ``min_period_between_procedures``
> +     - ``30``
> +     - Minimum time between consecutive procedures
> +   * - ``max_period_between_procedures``
> +     - ``150``
> +     - Maximum time between consecutive procedures
> +   * - ``max_procedure_count``
> +     - ``0``
> +     - Max number of procedures; ``0`` = no limit
> +   * - ``min_sub_event_len``
> +     - ``00:20:00``
> +     - Min CS subevent length, 3-byte LE (colon-separated hex)
> +   * - ``max_sub_event_len``
> +     - ``03:20:00``
> +     - Max CS subevent length, 3-byte LE (colon-separated hex)
> +   * - ``tone_antenna_config_selection``
> +     - ``0x07``
> +     - Antenna config for CS tone exchanges
> +   * - ``phy``
> +     - ``0x01``
> +     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
> +   * - ``tx_power_delta``
> +     - ``0x80``
> +     - Remote vs local TX power delta; ``0x80`` = not applicable
> +   * - ``preferred_peer_antenna``
> +     - ``0x03``
> +     - Preferred antenna for the peer device
> +   * - ``snr_control_initiator``
> +     - ``0xFF``
> +     - SNR control for initiator; ``0xFF`` = no preference
> +   * - ``snr_control_reflector``
> +     - ``0xFF``
> +     - SNR control for reflector; ``0xFF`` = no preference
> +
> +:Example Start with all defaults, no timeout:
> +       | **> start**
> +:Example Start on a specific device:
> +       | **> start AA:BB:CC:DD:EE:FF**
> +:Example Start on a specific device with 10-second auto-stop:
> +       | **> start AA:BB:CC:DD:EE:FF 10**
> +:Example Start with 10-second auto-stop (single device, address omitted):
> +       | **> start 0 10**
> +:Example Start with no timeout, explicit:
> +       | **> start**
> +:Example Start with 5-minute auto-stop:
> +       | **> start AA:BB:CC:DD:EE:FF 300**
> +:Example Start as Initiator only:
> +       | **> start role=0x01**
> +:Example Start as Reflector only:
> +       | **> start role=0x02**
> +:Example Start as both Initiator and Reflector:
> +       | **> start role=0x03**
> +:Example Start with Mode 2 (PBR) main mode:
> +       | **> start main_mode_type=2**
> +:Example Start with both RTT and PBR modes:
> +       | **> start main_mode_type=3**
> +:Example Start with LE 2M PHY for CS procedures:
> +       | **> start phy=0x02**
> +:Example Start with LE 2M PHY for both CS sync and procedures:
> +       | **> start cs_sync_phy=0x02 phy=0x02**
> +:Example Start with reduced TX power (10 dBm):
> +       | **> start max_tx_power=10**
> +:Example Start with companion signal enabled:
> +       | **> start companion_signal_enable=1**
> +:Example Start with a procedure limit of 100:
> +       | **> start max_procedure_count=100**
> +:Example Start with high SNR preference on both roles:
> +       | **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
> +:Example Start with custom channel map (all enabled):
> +       | **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
> +:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
> +       | **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
> +:Example Start both roles, Mode 1, no limit, custom step counts:
> +       | **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
> +:Example Start on a device, Reflector, 60-second timeout:
> +       | **> start AA:BB:CC:DD:EE:FF 60 role=0x02**

This approach is too complicated, and users will struggle to discover
available commands without consulting the documentation. Although it
offers flexibility to specify only necessary parameters for a session,
this quickly becomes an individual setting abstraction similar to
`defset`.

> +defset
> +------
> +
> +Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
> +``max_tx_power``) on the connected device without starting a
> +measurement. This is required for the Reflector role: a Reflector
> +never calls **start** because it waits passively for the remote
> +Initiator, so **defset** is the only way to push these settings to
> +the daemon before the remote side initiates the procedure.
> +
> +Any ``param=value`` arguments update the local parameter state and
> +are immediately sent to the daemon via **SetDefaultSettings**.

I don't think this is going to work. `SetDefaultSettings` would need
to remember what each client has set in case there are multiple
clients, otherwise they would overwrite one another. Instead, this
should be stored locally and then set as properties to
StartMeasurement.

> +Omitting all arguments sends the current local values unchanged.
> +
> +:Usage: **> defset [param=value ...]**
> +:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
> +:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
> +               ``max_tx_power`` (optional)
> +:Example Configure as Reflector:
> +       | **> defset role=0x02**
> +:Example Configure as Both with reduced TX power:
> +       | **> defset role=0x03 max_tx_power=10**
> +:Example Apply current local values without changing them:
> +       | **> defset**

Like I said above, this will make it hard for users to discover what
to enter without consulting the documentation. Instead, each file
should probably have a dedicated command, eg `cs.role
<initiator/reflector,both>`, etc for all the settings necessary for
cs.start to work.

> +stop
> +----
> +
> +Stops an active CS distance measurement. When only one measurement is
> +running the device address may be omitted. When multiple measurements
> +are active the address is required to identify which one to stop.
> +
> +:Usage: **> stop [dev_addr]**
> +:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
> +:[dev_addr]: Bluetooth address of the device to stop (optional when
> +             only one session is active; required otherwise)
> +:Example Stop the only active measurement:
> +       | **> stop**
> +:Example Stop a specific device when multiple are active:
> +       | **> stop AA:BB:CC:DD:EE:FF**
> +:Example Stop a second device:
> +       | **> stop 11:22:33:44:55:66**
> +
> +show
> +----
> +
> +Displays all active measurements (device path for each) and the full
> +set of CS parameter values that will be used on the next **start** call.
> +When no measurements are active, ``none`` is shown.
> +
> +The parameter output is divided into three sections:
> +
> +- **Default Settings** — role, CS sync antenna selection, max TX power.
> +- **CS Config Params** — per-procedure configuration fields including
> +  mode type, step counts, PHY, and channel map.
> +- **CS Frequency Params** — procedure scheduling fields including
> +  duration, period, subevent lengths, and SNR control.
> +
> +:Usage: **> show**
> +:Example Show active session and all CS parameters:
> +       | **> show**
> +
> +RESOURCES
> +=========
> +
> +http://www.bluez.org
> +
> +REPORTING BUGS
> +==============
> +
> +linux-bluetooth@vger.kernel.org
> diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
> new file mode 100644
> index 000000000..73f484507
> --- /dev/null
> +++ b/doc/org.bluez.ChannelSounding1.rst
> @@ -0,0 +1,302 @@
> +==========================
> +org.bluez.ChannelSounding1
> +==========================
> +
> +----------------------------------------------
> +BlueZ D-Bus Channel Sounding API documentation
> +----------------------------------------------
> +
> +:Version: BlueZ
> +:Date: June 2026
> +:Manual section: 5
> +:Manual group: Linux System Administration
> +
> +Interface
> +=========
> +
> +:Service:      org.bluez
> +:Interface:    org.bluez.ChannelSounding1
> +:Object path:  [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> +:Used by:      **bluetoothctl(1)**, **bluetoothctl-cs(1)**
> +
> +Methods
> +-------
> +
> +void StartMeasurement(dict params)
> +``````````````````````````````````
> +
> +Starts a Channel Sounding distance measurement procedure on the connected
> +device. All configuration is supplied in a single ``a{sv}`` dictionary.
> +Any key that is omitted retains its current value in the daemon.
> +
> +The device to measure is identified by the D-Bus object path on which
> +this method is called
> +(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
> +Only one measurement per device object may be active at a time. Calling
> +**StartMeasurement** while a session is already active returns
> +``org.bluez.Error.InProgress``.
> +
> +Supported dictionary keys:
> +
> +:uint32 duration_secs (Default: 0):
> +
> +       Duration in seconds before the measurement is stopped
> +       automatically. A value of 0 disables the automatic timeout.
> +
> +:byte role (Default: 0x03):
> +
> +       CS role to use for the measurement.
> +
> +       Possible values:
> +
> +       :0x01: Initiator
> +       :0x02: Reflector
> +       :0x03: Both (Initiator and Reflector)
> +
> +:byte cs_sync_ant_sel (Default: 0xFF):
> +
> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> +       by the Bluetooth specification.
> +
> +:byte max_tx_power (Default: 0x14):
> +
> +       Maximum TX power in dBm, treated as a signed value. Valid
> +       range is -127 to +20 dBm.
> +
> +:byte config_id:
> +
> +       CS configuration identifier.
> +
> +:byte main_mode_type:
> +
> +       Main CS mode used in the procedure.
> +
> +:byte sub_mode_type:
> +
> +       Sub-mode within the main mode. Set to 0xFF when unused.
> +
> +:byte main_mode_min_steps:
> +
> +       Minimum number of CS main mode steps per CS subevent.
> +
> +:byte main_mode_max_steps:
> +
> +       Maximum number of CS main mode steps per CS subevent.
> +
> +:byte main_mode_repetition:
> +
> +       Number of times the main mode steps are repeated in a
> +       subevent.
> +
> +:byte mode0_steps:
> +
> +       Number of CS Mode 0 steps at the beginning of each subevent.
> +
> +:byte rtt_types:
> +
> +       Round Trip Time measurement types for the configuration.
> +
> +:byte cs_sync_phy:
> +
> +       PHY used for CS sync packets.
> +
> +       Possible values:
> +
> +       :0x01: LE 1M PHY
> +       :0x02: LE 2M PHY
> +
> +:array{byte} channel_map:
> +
> +       10-byte channel map bitmap. Must be exactly 10 bytes.
> +
> +:byte channel_map_repetition:
> +
> +       Number of consecutive repetitions of the channel map.
> +
> +:byte channel_selection_type:
> +
> +       Algorithm used for CS channel selection.
> +
> +:byte channel_shape:
> +
> +       Shape used in the channel selection algorithm.
> +
> +:byte channel_jump:
> +
> +       Channel jump size used in the channel selection algorithm.
> +
> +:byte companion_signal_enable:
> +
> +       Set to 1 to transmit a companion signal alongside the CS
> +       tone, 0 to disable.
> +
> +:uint16 max_procedure_duration:
> +
> +       Maximum duration of a single CS measurement procedure.
> +
> +:uint16 min_period_between_procedures:
> +
> +       Minimum time between consecutive CS measurement procedures.
> +
> +:uint16 max_period_between_procedures:
> +
> +       Maximum time between consecutive CS measurement procedures.
> +
> +:uint16 max_procedure_count:
> +
> +       Maximum number of CS measurement procedures to run.
> +       A value of 0 means no limit.
> +
> +:array{byte} min_sub_event_len:
> +
> +       Minimum CS subevent length as a 3-byte little-endian value.
> +       Must be exactly 3 bytes.
> +
> +:array{byte} max_sub_event_len:
> +
> +       Maximum CS subevent length as a 3-byte little-endian value.
> +       Must be exactly 3 bytes.
> +
> +:byte tone_antenna_config_selection:
> +
> +       Antenna configuration used for CS tone exchanges.
> +
> +:byte phy:
> +
> +       PHY used during CS procedures.
> +
> +       Possible values:
> +
> +       :0x01: LE 1M PHY
> +       :0x02: LE 2M PHY
> +
> +:byte tx_power_delta:
> +
> +       Difference between remote and local TX power during CS
> +       procedures. 0x80 indicates not applicable.
> +
> +:byte preferred_peer_antenna:
> +
> +       Preferred antenna to be used by the peer device.
> +
> +:byte snr_control_initiator:
> +
> +       SNR control setting for the initiator role.
> +       0xFF indicates no preference.
> +
> +:byte snr_control_reflector:
> +
> +       SNR control setting for the reflector role.
> +       0xFF indicates no preference.
> +
> +Possible errors:
> +
> +:org.bluez.Error.InProgress:
> +:org.bluez.Error.InvalidArgs:
> +:org.freedesktop.DBus.Error.Failed:
> +
> +Examples:
> +
> +:bluetoothctl set role then start:
> +       | [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
> +:bluetoothctl start with defaults:
> +       | [cs] > start [dev_addr [duration_secs]]
> +
> +void SetDefaultSettings(dict params)
> +`````````````````````````````````````
> +
> +Sets the CS default settings for this device without starting a
> +measurement. This method is intended for the Reflector role, where
> +the device waits passively for the remote Initiator to begin the
> +procedure and therefore never calls **StartMeasurement**. It allows
> +the application to configure ``role``, ``cs_sync_ant_sel``, and
> +``max_tx_power`` ahead of time so they are in effect when the
> +controller processes the remote CS configuration.
> +Need this to set default settings in Reflector role.

There's no need for this if StartMeasurement already accepts the same
parameters. Each client should be capable of remembering the
parameters it used, so we don't bloat the daemon with them.

> +Supported dictionary keys:
> +
> +:byte role (Default: 0x03):
> +
> +       CS role to use for the measurement.
> +
> +       Possible values:
> +
> +       :0x01: Initiator
> +       :0x02: Reflector
> +       :0x03: Both (Initiator and Reflector)
> +
> +:byte cs_sync_ant_sel (Default: 0xFF):
> +
> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> +       by the Bluetooth specification.
> +
> +:byte max_tx_power (Default: 0x14):
> +
> +       Maximum TX power in dBm, treated as a signed value. Valid
> +       range is -127 to +20 dBm.
> +
> +Possible errors:
> +
> +:org.bluez.Error.InvalidArgs:
> +:org.freedesktop.DBus.Error.Failed:
> +
> +Examples:
> +
> +:bluetoothctl configure as Reflector:
> +       | [cs] > defset role=0x02
> +
> +void StopMeasurement(void)
> +``````````````````````````
> +
> +Stops the active Channel Sounding distance measurement on this device.
> +The device is identified by the D-Bus object path on which this method
> +is called — no session identifier is required.
> +
> +Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
> +
> +Possible errors:
> +
> +:org.bluez.Error.NotConnected:
> +:org.freedesktop.DBus.Error.Failed:
> +
> +In **bluetoothctl(1)**, the device address argument may be omitted only
> +when a single measurement is active; it is required when multiple
> +measurements are active.
> +
> +Examples:
> +
> +:bluetoothctl stop the only active measurement:
> +       | [cs] > stop
> +:bluetoothctl stop a specific device when multiple are active:
> +       | [cs] > stop AA:BB:CC:DD:EE:FF
> +
> +Properties
> +----------
> +
> +boolean Active [readonly]
> +`````````````````````````
> +
> +Indicates whether a CS distance measurement procedure is currently
> +active on this device.
> +
> +Set to ``true`` when a procedure starts — either because the local
> +Initiator called **StartMeasurement** successfully, or because the
> +remote Initiator enabled a CS procedure on the local Reflector.
> +
> +Set to ``false`` when the procedure stops for any reason: the local
> +application called **StopMeasurement**, the measurement duration timer
> +expired, or the ACL connection was dropped.
> +
> +This property emits ``PropertiesChanged`` on every transition so that
> +clients can track measurement state without polling.

What about the results? Shouldn't results be announced when the
procedure starts?

> +RESOURCES
> +=========
> +
> +http://www.bluez.org
> +
> +REPORTING BUGS
> +==============
> +
> +linux-bluetooth@vger.kernel.org
> --
>


-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-06 20:36   ` Luiz Augusto von Dentz
@ 2026-07-07  7:34     ` Naga Bhavani Akella
  2026-07-07 14:43       ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-07  7:34 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg

Hi Luiz,

Please find the comments inline

On 7/7/2026 2:06 AM, Luiz Augusto von Dentz wrote:
> Hi Naga,
> 
> On Mon, Jul 6, 2026 at 12:16 PM Naga Bhavani Akella
> <naga.akella@oss.qualcomm.com> wrote:
>>
>> Add :Used by: field linking to bluetoothctl cs submenu and
>> Examples section showing corresponding bluetoothctl cs commands
>> for D-Bus methods
>> ---
>>  doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
>>  doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++
> 
> These 2 needs to be splitted, each file on its own commit.
sure, will do that> 
>>  2 files changed, 580 insertions(+)
>>  create mode 100644 doc/bluetoothctl-cs.rst
>>  create mode 100644 doc/org.bluez.ChannelSounding1.rst
>>
>> diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
>> new file mode 100644
>> index 000000000..c16c89298
>> --- /dev/null
>> +++ b/doc/bluetoothctl-cs.rst
>> @@ -0,0 +1,278 @@
>> +================
>> +bluetoothctl-cs
>> +================
>> +
>> +--------------------------
>> +Channel Sounding Submenu
>> +--------------------------
>> +
>> +:Version: BlueZ
>> +:Copyright: Free use of this software is granted under the terms of the GNU
>> +            Lesser General Public Licenses (LGPL).
>> +:Date: June 2026
>> +:Manual section: 1
>> +:Manual group: Linux System Administration
>> +
>> +SYNOPSIS
>> +========
>> +
>> +**bluetoothctl** [--options] [cs.commands]
>> +
>> +This submenu controls Bluetooth Channel Sounding (CS) distance measurement
>> +using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
>> +starting and stopping measurements and inspecting the current parameter
>> +state and active session identifier.
>> +
>> +CS parameters can be overridden on any **start** call using inline
>> +``param=value`` arguments. Overrides are applied to the local parameter
>> +state before the measurement is started, so **show** reflects them
>> +immediately after.
>> +
>> +
>> +Channel Sounding Commands
>> +=========================
>> +
>> +start
>> +-----
>> +
>> +Sets one or more CS parameters and starts a distance measurement on the
>> +connected device. All configuration is sent to the daemon in a single
>> +**StartMeasurement** call. On success the device path is printed to
>> +the console. Multiple simultaneous sessions across different devices are
>> +supported; each is tracked independently.
>> +
>> +Calling **start** on a device that already has an active measurement
>> +returns an error without starting a second session on the same device.
>> +
>> +Positional arguments are optional and must appear before any
>> +``param=value`` pairs:
>> +
>> +- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
>> +  no timeout.
>> +
>> +Any additional argument of the form ``param=value`` overrides the named
>> +parameter for this call and for all subsequent calls. Array-valued
>> +parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
>> +use colon-separated hex bytes with no ``0x`` prefix.
>> +
>> +:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
>> +:[dev_addr]: Bluetooth address of the target device (optional; uses the
>> +             only available CS-capable device when omitted)
>> +:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
>> +:[param=value]: One or more ``param=value`` overrides (optional)
>> +
>> +**Settable parameters:**
>> +
>> +.. list-table::
>> +   :header-rows: 1
>> +   :widths: 35 15 50
>> +
>> +   * - Parameter
>> +     - Default
>> +     - Description
>> +   * - ``role``
>> +     - ``0x03``
>> +     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
>> +   * - ``cs_sync_ant_sel``
>> +     - ``0xFF``
>> +     - CS sync antenna selection (0xFE/0xFF reserved)
>> +   * - ``max_tx_power``
>> +     - ``20``
>> +     - Max TX power in dBm (signed, range −127 to +20)
>> +   * - ``config_id``
>> +     - ``0``
>> +     - CS configuration identifier
>> +   * - ``main_mode_type``
>> +     - ``1``
>> +     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
>> +   * - ``sub_mode_type``
>> +     - ``0xFF``
>> +     - Sub-mode within main mode; ``0xFF`` = unused
>> +   * - ``main_mode_min_steps``
>> +     - ``2``
>> +     - Min CS main mode steps per subevent
>> +   * - ``main_mode_max_steps``
>> +     - ``3``
>> +     - Max CS main mode steps per subevent
>> +   * - ``main_mode_repetition``
>> +     - ``1``
>> +     - Times main mode steps are repeated in a subevent
>> +   * - ``mode0_steps``
>> +     - ``2``
>> +     - CS Mode 0 steps at the beginning of each subevent
>> +   * - ``rtt_types``
>> +     - ``0``
>> +     - RTT measurement types bitmask
>> +   * - ``cs_sync_phy``
>> +     - ``0x01``
>> +     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
>> +   * - ``channel_map``
>> +     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
>> +     - 10-byte channel map bitmap (colon-separated hex)
>> +   * - ``channel_map_repetition``
>> +     - ``1``
>> +     - Consecutive repetitions of the channel map
>> +   * - ``channel_selection_type``
>> +     - ``0``
>> +     - CS channel selection algorithm
>> +   * - ``channel_shape``
>> +     - ``0``
>> +     - Shape used in channel selection algorithm
>> +   * - ``channel_jump``
>> +     - ``2``
>> +     - Channel jump size
>> +   * - ``companion_signal_enable``
>> +     - ``0``
>> +     - ``1`` to transmit companion signal, ``0`` to disable
>> +   * - ``max_procedure_duration``
>> +     - ``1600``
>> +     - Maximum duration of one CS measurement procedure
>> +   * - ``min_period_between_procedures``
>> +     - ``30``
>> +     - Minimum time between consecutive procedures
>> +   * - ``max_period_between_procedures``
>> +     - ``150``
>> +     - Maximum time between consecutive procedures
>> +   * - ``max_procedure_count``
>> +     - ``0``
>> +     - Max number of procedures; ``0`` = no limit
>> +   * - ``min_sub_event_len``
>> +     - ``00:20:00``
>> +     - Min CS subevent length, 3-byte LE (colon-separated hex)
>> +   * - ``max_sub_event_len``
>> +     - ``03:20:00``
>> +     - Max CS subevent length, 3-byte LE (colon-separated hex)
>> +   * - ``tone_antenna_config_selection``
>> +     - ``0x07``
>> +     - Antenna config for CS tone exchanges
>> +   * - ``phy``
>> +     - ``0x01``
>> +     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
>> +   * - ``tx_power_delta``
>> +     - ``0x80``
>> +     - Remote vs local TX power delta; ``0x80`` = not applicable
>> +   * - ``preferred_peer_antenna``
>> +     - ``0x03``
>> +     - Preferred antenna for the peer device
>> +   * - ``snr_control_initiator``
>> +     - ``0xFF``
>> +     - SNR control for initiator; ``0xFF`` = no preference
>> +   * - ``snr_control_reflector``
>> +     - ``0xFF``
>> +     - SNR control for reflector; ``0xFF`` = no preference
>> +
>> +:Example Start with all defaults, no timeout:
>> +       | **> start**
>> +:Example Start on a specific device:
>> +       | **> start AA:BB:CC:DD:EE:FF**
>> +:Example Start on a specific device with 10-second auto-stop:
>> +       | **> start AA:BB:CC:DD:EE:FF 10**
>> +:Example Start with 10-second auto-stop (single device, address omitted):
>> +       | **> start 0 10**
>> +:Example Start with no timeout, explicit:
>> +       | **> start**
>> +:Example Start with 5-minute auto-stop:
>> +       | **> start AA:BB:CC:DD:EE:FF 300**
>> +:Example Start as Initiator only:
>> +       | **> start role=0x01**
>> +:Example Start as Reflector only:
>> +       | **> start role=0x02**
>> +:Example Start as both Initiator and Reflector:
>> +       | **> start role=0x03**
>> +:Example Start with Mode 2 (PBR) main mode:
>> +       | **> start main_mode_type=2**
>> +:Example Start with both RTT and PBR modes:
>> +       | **> start main_mode_type=3**
>> +:Example Start with LE 2M PHY for CS procedures:
>> +       | **> start phy=0x02**
>> +:Example Start with LE 2M PHY for both CS sync and procedures:
>> +       | **> start cs_sync_phy=0x02 phy=0x02**
>> +:Example Start with reduced TX power (10 dBm):
>> +       | **> start max_tx_power=10**
>> +:Example Start with companion signal enabled:
>> +       | **> start companion_signal_enable=1**
>> +:Example Start with a procedure limit of 100:
>> +       | **> start max_procedure_count=100**
>> +:Example Start with high SNR preference on both roles:
>> +       | **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
>> +:Example Start with custom channel map (all enabled):
>> +       | **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
>> +:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
>> +       | **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
>> +:Example Start both roles, Mode 1, no limit, custom step counts:
>> +       | **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
>> +:Example Start on a device, Reflector, 60-second timeout:
>> +       | **> start AA:BB:CC:DD:EE:FF 60 role=0x02**
> 
> This approach is too complicated, and users will struggle to discover
> available commands without consulting the documentation. Although it
> offers flexibility to specify only necessary parameters for a session,
> this quickly becomes an individual setting abstraction similar to
> `defset`.
We already provide default values for all parameters and expose them
through the 'show' CS menu option, so users can easily see the
currently configured settings without referring to external documentation.
If a user wants to modify any parameter, they can do so through
the startmeasurement option. Additionally, only the
remote device address and timeout parameters are mandatory.
All other parameters are optional and will use their default values
if not explicitly specified.
This keeps the command usage simple while still providing the flexibility
to override only the settings that are relevant for a particular session.
> 
>> +defset
>> +------
>> +
>> +Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
>> +``max_tx_power``) on the connected device without starting a
>> +measurement. This is required for the Reflector role: a Reflector
>> +never calls **start** because it waits passively for the remote
>> +Initiator, so **defset** is the only way to push these settings to
>> +the daemon before the remote side initiates the procedure.
>> +
>> +Any ``param=value`` arguments update the local parameter state and
>> +are immediately sent to the daemon via **SetDefaultSettings**.
> 
> I don't think this is going to work. `SetDefaultSettings` would need
> to remember what each client has set in case there are multiple
> clients, otherwise they would overwrite one another. Instead, this
> should be stored locally and then set as properties to
> StartMeasurement.
We have introduced the Defset option specifically for the Reflector role
because Start Measurement is not applicable in this role.
If users want to configure default settings while operating
in the Reflector role, they should have the option to do so
through the Defset option. Defset command also accepts the device path
as an input parameter, which allows it to distinguish between multiple clients. > 
>> +Omitting all arguments sends the current local values unchanged.
>> +
>> +:Usage: **> defset [param=value ...]**
>> +:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
>> +:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
>> +               ``max_tx_power`` (optional)
>> +:Example Configure as Reflector:
>> +       | **> defset role=0x02**
>> +:Example Configure as Both with reduced TX power:
>> +       | **> defset role=0x03 max_tx_power=10**
>> +:Example Apply current local values without changing them:
>> +       | **> defset**
> 
> Like I said above, this will make it hard for users to discover what
> to enter without consulting the documentation. Instead, each file
> should probably have a dedicated command, eg `cs.role
> <initiator/reflector,both>`, etc for all the settings necessary for
> cs.start to work.
> 
>> +stop
>> +----
>> +
>> +Stops an active CS distance measurement. When only one measurement is
>> +running the device address may be omitted. When multiple measurements
>> +are active the address is required to identify which one to stop.
>> +
>> +:Usage: **> stop [dev_addr]**
>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
>> +:[dev_addr]: Bluetooth address of the device to stop (optional when
>> +             only one session is active; required otherwise)
>> +:Example Stop the only active measurement:
>> +       | **> stop**
>> +:Example Stop a specific device when multiple are active:
>> +       | **> stop AA:BB:CC:DD:EE:FF**
>> +:Example Stop a second device:
>> +       | **> stop 11:22:33:44:55:66**
>> +
>> +show
>> +----
>> +
>> +Displays all active measurements (device path for each) and the full
>> +set of CS parameter values that will be used on the next **start** call.
>> +When no measurements are active, ``none`` is shown.
>> +
>> +The parameter output is divided into three sections:
>> +
>> +- **Default Settings** — role, CS sync antenna selection, max TX power.
>> +- **CS Config Params** — per-procedure configuration fields including
>> +  mode type, step counts, PHY, and channel map.
>> +- **CS Frequency Params** — procedure scheduling fields including
>> +  duration, period, subevent lengths, and SNR control.
>> +
>> +:Usage: **> show**
>> +:Example Show active session and all CS parameters:
>> +       | **> show**
>> +
>> +RESOURCES
>> +=========
>> +
>> +http://www.bluez.org
>> +
>> +REPORTING BUGS
>> +==============
>> +
>> +linux-bluetooth@vger.kernel.org
>> diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
>> new file mode 100644
>> index 000000000..73f484507
>> --- /dev/null
>> +++ b/doc/org.bluez.ChannelSounding1.rst
>> @@ -0,0 +1,302 @@
>> +==========================
>> +org.bluez.ChannelSounding1
>> +==========================
>> +
>> +----------------------------------------------
>> +BlueZ D-Bus Channel Sounding API documentation
>> +----------------------------------------------
>> +
>> +:Version: BlueZ
>> +:Date: June 2026
>> +:Manual section: 5
>> +:Manual group: Linux System Administration
>> +
>> +Interface
>> +=========
>> +
>> +:Service:      org.bluez
>> +:Interface:    org.bluez.ChannelSounding1
>> +:Object path:  [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
>> +:Used by:      **bluetoothctl(1)**, **bluetoothctl-cs(1)**
>> +
>> +Methods
>> +-------
>> +
>> +void StartMeasurement(dict params)
>> +``````````````````````````````````
>> +
>> +Starts a Channel Sounding distance measurement procedure on the connected
>> +device. All configuration is supplied in a single ``a{sv}`` dictionary.
>> +Any key that is omitted retains its current value in the daemon.
>> +
>> +The device to measure is identified by the D-Bus object path on which
>> +this method is called
>> +(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
>> +Only one measurement per device object may be active at a time. Calling
>> +**StartMeasurement** while a session is already active returns
>> +``org.bluez.Error.InProgress``.
>> +
>> +Supported dictionary keys:
>> +
>> +:uint32 duration_secs (Default: 0):
>> +
>> +       Duration in seconds before the measurement is stopped
>> +       automatically. A value of 0 disables the automatic timeout.
>> +
>> +:byte role (Default: 0x03):
>> +
>> +       CS role to use for the measurement.
>> +
>> +       Possible values:
>> +
>> +       :0x01: Initiator
>> +       :0x02: Reflector
>> +       :0x03: Both (Initiator and Reflector)
>> +
>> +:byte cs_sync_ant_sel (Default: 0xFF):
>> +
>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
>> +       by the Bluetooth specification.
>> +
>> +:byte max_tx_power (Default: 0x14):
>> +
>> +       Maximum TX power in dBm, treated as a signed value. Valid
>> +       range is -127 to +20 dBm.
>> +
>> +:byte config_id:
>> +
>> +       CS configuration identifier.
>> +
>> +:byte main_mode_type:
>> +
>> +       Main CS mode used in the procedure.
>> +
>> +:byte sub_mode_type:
>> +
>> +       Sub-mode within the main mode. Set to 0xFF when unused.
>> +
>> +:byte main_mode_min_steps:
>> +
>> +       Minimum number of CS main mode steps per CS subevent.
>> +
>> +:byte main_mode_max_steps:
>> +
>> +       Maximum number of CS main mode steps per CS subevent.
>> +
>> +:byte main_mode_repetition:
>> +
>> +       Number of times the main mode steps are repeated in a
>> +       subevent.
>> +
>> +:byte mode0_steps:
>> +
>> +       Number of CS Mode 0 steps at the beginning of each subevent.
>> +
>> +:byte rtt_types:
>> +
>> +       Round Trip Time measurement types for the configuration.
>> +
>> +:byte cs_sync_phy:
>> +
>> +       PHY used for CS sync packets.
>> +
>> +       Possible values:
>> +
>> +       :0x01: LE 1M PHY
>> +       :0x02: LE 2M PHY
>> +
>> +:array{byte} channel_map:
>> +
>> +       10-byte channel map bitmap. Must be exactly 10 bytes.
>> +
>> +:byte channel_map_repetition:
>> +
>> +       Number of consecutive repetitions of the channel map.
>> +
>> +:byte channel_selection_type:
>> +
>> +       Algorithm used for CS channel selection.
>> +
>> +:byte channel_shape:
>> +
>> +       Shape used in the channel selection algorithm.
>> +
>> +:byte channel_jump:
>> +
>> +       Channel jump size used in the channel selection algorithm.
>> +
>> +:byte companion_signal_enable:
>> +
>> +       Set to 1 to transmit a companion signal alongside the CS
>> +       tone, 0 to disable.
>> +
>> +:uint16 max_procedure_duration:
>> +
>> +       Maximum duration of a single CS measurement procedure.
>> +
>> +:uint16 min_period_between_procedures:
>> +
>> +       Minimum time between consecutive CS measurement procedures.
>> +
>> +:uint16 max_period_between_procedures:
>> +
>> +       Maximum time between consecutive CS measurement procedures.
>> +
>> +:uint16 max_procedure_count:
>> +
>> +       Maximum number of CS measurement procedures to run.
>> +       A value of 0 means no limit.
>> +
>> +:array{byte} min_sub_event_len:
>> +
>> +       Minimum CS subevent length as a 3-byte little-endian value.
>> +       Must be exactly 3 bytes.
>> +
>> +:array{byte} max_sub_event_len:
>> +
>> +       Maximum CS subevent length as a 3-byte little-endian value.
>> +       Must be exactly 3 bytes.
>> +
>> +:byte tone_antenna_config_selection:
>> +
>> +       Antenna configuration used for CS tone exchanges.
>> +
>> +:byte phy:
>> +
>> +       PHY used during CS procedures.
>> +
>> +       Possible values:
>> +
>> +       :0x01: LE 1M PHY
>> +       :0x02: LE 2M PHY
>> +
>> +:byte tx_power_delta:
>> +
>> +       Difference between remote and local TX power during CS
>> +       procedures. 0x80 indicates not applicable.
>> +
>> +:byte preferred_peer_antenna:
>> +
>> +       Preferred antenna to be used by the peer device.
>> +
>> +:byte snr_control_initiator:
>> +
>> +       SNR control setting for the initiator role.
>> +       0xFF indicates no preference.
>> +
>> +:byte snr_control_reflector:
>> +
>> +       SNR control setting for the reflector role.
>> +       0xFF indicates no preference.
>> +
>> +Possible errors:
>> +
>> +:org.bluez.Error.InProgress:
>> +:org.bluez.Error.InvalidArgs:
>> +:org.freedesktop.DBus.Error.Failed:
>> +
>> +Examples:
>> +
>> +:bluetoothctl set role then start:
>> +       | [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
>> +:bluetoothctl start with defaults:
>> +       | [cs] > start [dev_addr [duration_secs]]
>> +
>> +void SetDefaultSettings(dict params)
>> +`````````````````````````````````````
>> +
>> +Sets the CS default settings for this device without starting a
>> +measurement. This method is intended for the Reflector role, where
>> +the device waits passively for the remote Initiator to begin the
>> +procedure and therefore never calls **StartMeasurement**. It allows
>> +the application to configure ``role``, ``cs_sync_ant_sel``, and
>> +``max_tx_power`` ahead of time so they are in effect when the
>> +controller processes the remote CS configuration.
>> +Need this to set default settings in Reflector role.
> 
> There's no need for this if StartMeasurement already accepts the same
> parameters. Each client should be capable of remembering the
> parameters it used, so we don't bloat the daemon with them.
> 
We have introduced the Defset option specifically for the Reflector role
because Start Measurement is not applicable in this role.
If users want to configure default settings while operating
in the Reflector role, they should have the option to do so
through the Defset option.
>> +Supported dictionary keys:
>> +
>> +:byte role (Default: 0x03):
>> +
>> +       CS role to use for the measurement.
>> +
>> +       Possible values:
>> +
>> +       :0x01: Initiator
>> +       :0x02: Reflector
>> +       :0x03: Both (Initiator and Reflector)
>> +
>> +:byte cs_sync_ant_sel (Default: 0xFF):
>> +
>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
>> +       by the Bluetooth specification.
>> +
>> +:byte max_tx_power (Default: 0x14):
>> +
>> +       Maximum TX power in dBm, treated as a signed value. Valid
>> +       range is -127 to +20 dBm.
>> +
>> +Possible errors:
>> +
>> +:org.bluez.Error.InvalidArgs:
>> +:org.freedesktop.DBus.Error.Failed:
>> +
>> +Examples:
>> +
>> +:bluetoothctl configure as Reflector:
>> +       | [cs] > defset role=0x02
>> +
>> +void StopMeasurement(void)
>> +``````````````````````````
>> +
>> +Stops the active Channel Sounding distance measurement on this device.
>> +The device is identified by the D-Bus object path on which this method
>> +is called — no session identifier is required.
>> +
>> +Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
>> +
>> +Possible errors:
>> +
>> +:org.bluez.Error.NotConnected:
>> +:org.freedesktop.DBus.Error.Failed:
>> +
>> +In **bluetoothctl(1)**, the device address argument may be omitted only
>> +when a single measurement is active; it is required when multiple
>> +measurements are active.
>> +
>> +Examples:
>> +
>> +:bluetoothctl stop the only active measurement:
>> +       | [cs] > stop
>> +:bluetoothctl stop a specific device when multiple are active:
>> +       | [cs] > stop AA:BB:CC:DD:EE:FF
>> +
>> +Properties
>> +----------
>> +
>> +boolean Active [readonly]
>> +`````````````````````````
>> +
>> +Indicates whether a CS distance measurement procedure is currently
>> +active on this device.
>> +
>> +Set to ``true`` when a procedure starts — either because the local
>> +Initiator called **StartMeasurement** successfully, or because the
>> +remote Initiator enabled a CS procedure on the local Reflector.
>> +
>> +Set to ``false`` when the procedure stops for any reason: the local
>> +application called **StopMeasurement**, the measurement duration timer
>> +expired, or the ACL connection was dropped.
>> +
>> +This property emits ``PropertiesChanged`` on every transition so that
>> +clients can track measurement state without polling.
> 
> What about the results? Shouldn't results be announced when the
> procedure starts?
This is still under discussion,
as the measurement calculations are handled by a separate daemon.> 
>> +RESOURCES
>> +=========
>> +
>> +http://www.bluez.org
>> +
>> +REPORTING BUGS
>> +==============
>> +
>> +linux-bluetooth@vger.kernel.org
>> --
>>
> 
> 


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

* Re: [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-07  7:34     ` Naga Bhavani Akella
@ 2026-07-07 14:43       ` Luiz Augusto von Dentz
  2026-07-08  8:52         ` Naga Bhavani Akella
  0 siblings, 1 reply; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-07 14:43 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg

Hi Naga,

On Tue, Jul 7, 2026 at 3:34 AM Naga Bhavani Akella
<naga.akella@oss.qualcomm.com> wrote:
>
> Hi Luiz,
>
> Please find the comments inline
>
> On 7/7/2026 2:06 AM, Luiz Augusto von Dentz wrote:
> > Hi Naga,
> >
> > On Mon, Jul 6, 2026 at 12:16 PM Naga Bhavani Akella
> > <naga.akella@oss.qualcomm.com> wrote:
> >>
> >> Add :Used by: field linking to bluetoothctl cs submenu and
> >> Examples section showing corresponding bluetoothctl cs commands
> >> for D-Bus methods
> >> ---
> >>  doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
> >>  doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++
> >
> > These 2 needs to be splitted, each file on its own commit.
> sure, will do that>
> >>  2 files changed, 580 insertions(+)
> >>  create mode 100644 doc/bluetoothctl-cs.rst
> >>  create mode 100644 doc/org.bluez.ChannelSounding1.rst
> >>
> >> diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
> >> new file mode 100644
> >> index 000000000..c16c89298
> >> --- /dev/null
> >> +++ b/doc/bluetoothctl-cs.rst
> >> @@ -0,0 +1,278 @@
> >> +================
> >> +bluetoothctl-cs
> >> +================
> >> +
> >> +--------------------------
> >> +Channel Sounding Submenu
> >> +--------------------------
> >> +
> >> +:Version: BlueZ
> >> +:Copyright: Free use of this software is granted under the terms of the GNU
> >> +            Lesser General Public Licenses (LGPL).
> >> +:Date: June 2026
> >> +:Manual section: 1
> >> +:Manual group: Linux System Administration
> >> +
> >> +SYNOPSIS
> >> +========
> >> +
> >> +**bluetoothctl** [--options] [cs.commands]
> >> +
> >> +This submenu controls Bluetooth Channel Sounding (CS) distance measurement
> >> +using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
> >> +starting and stopping measurements and inspecting the current parameter
> >> +state and active session identifier.
> >> +
> >> +CS parameters can be overridden on any **start** call using inline
> >> +``param=value`` arguments. Overrides are applied to the local parameter
> >> +state before the measurement is started, so **show** reflects them
> >> +immediately after.
> >> +
> >> +
> >> +Channel Sounding Commands
> >> +=========================
> >> +
> >> +start
> >> +-----
> >> +
> >> +Sets one or more CS parameters and starts a distance measurement on the
> >> +connected device. All configuration is sent to the daemon in a single
> >> +**StartMeasurement** call. On success the device path is printed to
> >> +the console. Multiple simultaneous sessions across different devices are
> >> +supported; each is tracked independently.
> >> +
> >> +Calling **start** on a device that already has an active measurement
> >> +returns an error without starting a second session on the same device.
> >> +
> >> +Positional arguments are optional and must appear before any
> >> +``param=value`` pairs:
> >> +
> >> +- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
> >> +  no timeout.
> >> +
> >> +Any additional argument of the form ``param=value`` overrides the named
> >> +parameter for this call and for all subsequent calls. Array-valued
> >> +parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
> >> +use colon-separated hex bytes with no ``0x`` prefix.
> >> +
> >> +:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
> >> +:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
> >> +:[dev_addr]: Bluetooth address of the target device (optional; uses the
> >> +             only available CS-capable device when omitted)
> >> +:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
> >> +:[param=value]: One or more ``param=value`` overrides (optional)
> >> +
> >> +**Settable parameters:**
> >> +
> >> +.. list-table::
> >> +   :header-rows: 1
> >> +   :widths: 35 15 50
> >> +
> >> +   * - Parameter
> >> +     - Default
> >> +     - Description
> >> +   * - ``role``
> >> +     - ``0x03``
> >> +     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
> >> +   * - ``cs_sync_ant_sel``
> >> +     - ``0xFF``
> >> +     - CS sync antenna selection (0xFE/0xFF reserved)
> >> +   * - ``max_tx_power``
> >> +     - ``20``
> >> +     - Max TX power in dBm (signed, range −127 to +20)
> >> +   * - ``config_id``
> >> +     - ``0``
> >> +     - CS configuration identifier
> >> +   * - ``main_mode_type``
> >> +     - ``1``
> >> +     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
> >> +   * - ``sub_mode_type``
> >> +     - ``0xFF``
> >> +     - Sub-mode within main mode; ``0xFF`` = unused
> >> +   * - ``main_mode_min_steps``
> >> +     - ``2``
> >> +     - Min CS main mode steps per subevent
> >> +   * - ``main_mode_max_steps``
> >> +     - ``3``
> >> +     - Max CS main mode steps per subevent
> >> +   * - ``main_mode_repetition``
> >> +     - ``1``
> >> +     - Times main mode steps are repeated in a subevent
> >> +   * - ``mode0_steps``
> >> +     - ``2``
> >> +     - CS Mode 0 steps at the beginning of each subevent
> >> +   * - ``rtt_types``
> >> +     - ``0``
> >> +     - RTT measurement types bitmask
> >> +   * - ``cs_sync_phy``
> >> +     - ``0x01``
> >> +     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
> >> +   * - ``channel_map``
> >> +     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
> >> +     - 10-byte channel map bitmap (colon-separated hex)
> >> +   * - ``channel_map_repetition``
> >> +     - ``1``
> >> +     - Consecutive repetitions of the channel map
> >> +   * - ``channel_selection_type``
> >> +     - ``0``
> >> +     - CS channel selection algorithm
> >> +   * - ``channel_shape``
> >> +     - ``0``
> >> +     - Shape used in channel selection algorithm
> >> +   * - ``channel_jump``
> >> +     - ``2``
> >> +     - Channel jump size
> >> +   * - ``companion_signal_enable``
> >> +     - ``0``
> >> +     - ``1`` to transmit companion signal, ``0`` to disable
> >> +   * - ``max_procedure_duration``
> >> +     - ``1600``
> >> +     - Maximum duration of one CS measurement procedure
> >> +   * - ``min_period_between_procedures``
> >> +     - ``30``
> >> +     - Minimum time between consecutive procedures
> >> +   * - ``max_period_between_procedures``
> >> +     - ``150``
> >> +     - Maximum time between consecutive procedures
> >> +   * - ``max_procedure_count``
> >> +     - ``0``
> >> +     - Max number of procedures; ``0`` = no limit
> >> +   * - ``min_sub_event_len``
> >> +     - ``00:20:00``
> >> +     - Min CS subevent length, 3-byte LE (colon-separated hex)
> >> +   * - ``max_sub_event_len``
> >> +     - ``03:20:00``
> >> +     - Max CS subevent length, 3-byte LE (colon-separated hex)
> >> +   * - ``tone_antenna_config_selection``
> >> +     - ``0x07``
> >> +     - Antenna config for CS tone exchanges
> >> +   * - ``phy``
> >> +     - ``0x01``
> >> +     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
> >> +   * - ``tx_power_delta``
> >> +     - ``0x80``
> >> +     - Remote vs local TX power delta; ``0x80`` = not applicable
> >> +   * - ``preferred_peer_antenna``
> >> +     - ``0x03``
> >> +     - Preferred antenna for the peer device
> >> +   * - ``snr_control_initiator``
> >> +     - ``0xFF``
> >> +     - SNR control for initiator; ``0xFF`` = no preference
> >> +   * - ``snr_control_reflector``
> >> +     - ``0xFF``
> >> +     - SNR control for reflector; ``0xFF`` = no preference
> >> +
> >> +:Example Start with all defaults, no timeout:
> >> +       | **> start**
> >> +:Example Start on a specific device:
> >> +       | **> start AA:BB:CC:DD:EE:FF**
> >> +:Example Start on a specific device with 10-second auto-stop:
> >> +       | **> start AA:BB:CC:DD:EE:FF 10**
> >> +:Example Start with 10-second auto-stop (single device, address omitted):
> >> +       | **> start 0 10**
> >> +:Example Start with no timeout, explicit:
> >> +       | **> start**
> >> +:Example Start with 5-minute auto-stop:
> >> +       | **> start AA:BB:CC:DD:EE:FF 300**
> >> +:Example Start as Initiator only:
> >> +       | **> start role=0x01**
> >> +:Example Start as Reflector only:
> >> +       | **> start role=0x02**
> >> +:Example Start as both Initiator and Reflector:
> >> +       | **> start role=0x03**
> >> +:Example Start with Mode 2 (PBR) main mode:
> >> +       | **> start main_mode_type=2**
> >> +:Example Start with both RTT and PBR modes:
> >> +       | **> start main_mode_type=3**
> >> +:Example Start with LE 2M PHY for CS procedures:
> >> +       | **> start phy=0x02**
> >> +:Example Start with LE 2M PHY for both CS sync and procedures:
> >> +       | **> start cs_sync_phy=0x02 phy=0x02**
> >> +:Example Start with reduced TX power (10 dBm):
> >> +       | **> start max_tx_power=10**
> >> +:Example Start with companion signal enabled:
> >> +       | **> start companion_signal_enable=1**
> >> +:Example Start with a procedure limit of 100:
> >> +       | **> start max_procedure_count=100**
> >> +:Example Start with high SNR preference on both roles:
> >> +       | **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
> >> +:Example Start with custom channel map (all enabled):
> >> +       | **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
> >> +:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
> >> +       | **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
> >> +:Example Start both roles, Mode 1, no limit, custom step counts:
> >> +       | **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
> >> +:Example Start on a device, Reflector, 60-second timeout:
> >> +       | **> start AA:BB:CC:DD:EE:FF 60 role=0x02**
> >
> > This approach is too complicated, and users will struggle to discover
> > available commands without consulting the documentation. Although it
> > offers flexibility to specify only necessary parameters for a session,
> > this quickly becomes an individual setting abstraction similar to
> > `defset`.
> We already provide default values for all parameters and expose them
> through the 'show' CS menu option, so users can easily see the
> currently configured settings without referring to external documentation.
> If a user wants to modify any parameter, they can do so through
> the startmeasurement option. Additionally, only the
> remote device address and timeout parameters are mandatory.
> All other parameters are optional and will use their default values
> if not explicitly specified.
> This keeps the command usage simple while still providing the flexibility
> to override only the settings that are relevant for a particular session.

I have to disagree that it makes the usage simple, it doesn't. It
makes its implementation more flexible but there is no way to discover
the parameters via tab completion, etc, The parameters are only
available via documentation because they are not commands themselves.

> >
> >> +defset
> >> +------
> >> +
> >> +Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
> >> +``max_tx_power``) on the connected device without starting a
> >> +measurement. This is required for the Reflector role: a Reflector
> >> +never calls **start** because it waits passively for the remote
> >> +Initiator, so **defset** is the only way to push these settings to
> >> +the daemon before the remote side initiates the procedure.
> >> +
> >> +Any ``param=value`` arguments update the local parameter state and
> >> +are immediately sent to the daemon via **SetDefaultSettings**.
> >
> > I don't think this is going to work. `SetDefaultSettings` would need
> > to remember what each client has set in case there are multiple
> > clients, otherwise they would overwrite one another. Instead, this
> > should be stored locally and then set as properties to
> > StartMeasurement.
> We have introduced the Defset option specifically for the Reflector role
> because Start Measurement is not applicable in this role.
> If users want to configure default settings while operating
> in the Reflector role, they should have the option to do so
> through the Defset option. Defset command also accepts the device path
> as an input parameter, which allows it to distinguish between multiple clients.

Well that means SetDefaultSettings needs to work as a registration
method, something we thought wouldn't be necessary. In that case we
should rename it to RegisterCapabilities and the command should be
cs.register and we will need to figure out how to decide which
capabilities to use if multiple capabilities are registered.
Alternatively, we could allow only one, but then we need to expose
what the default settings are.

Anyway, the point of having each setting as a dedicated command still
stands regardless of whether we implement a `cs.` namespace.register
or perhaps make cs.start work as a reflector in which case it will not
need any parameters.

 >
> >> +Omitting all arguments sends the current local values unchanged.
> >> +
> >> +:Usage: **> defset [param=value ...]**
> >> +:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
> >> +:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
> >> +               ``max_tx_power`` (optional)
> >> +:Example Configure as Reflector:
> >> +       | **> defset role=0x02**
> >> +:Example Configure as Both with reduced TX power:
> >> +       | **> defset role=0x03 max_tx_power=10**
> >> +:Example Apply current local values without changing them:
> >> +       | **> defset**
> >
> > Like I said above, this will make it hard for users to discover what
> > to enter without consulting the documentation. Instead, each file
> > should probably have a dedicated command, eg `cs.role
> > <initiator/reflector,both>`, etc for all the settings necessary for
> > cs.start to work.
> >
> >> +stop
> >> +----
> >> +
> >> +Stops an active CS distance measurement. When only one measurement is
> >> +running the device address may be omitted. When multiple measurements
> >> +are active the address is required to identify which one to stop.
> >> +
> >> +:Usage: **> stop [dev_addr]**
> >> +:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
> >> +:[dev_addr]: Bluetooth address of the device to stop (optional when
> >> +             only one session is active; required otherwise)
> >> +:Example Stop the only active measurement:
> >> +       | **> stop**
> >> +:Example Stop a specific device when multiple are active:
> >> +       | **> stop AA:BB:CC:DD:EE:FF**
> >> +:Example Stop a second device:
> >> +       | **> stop 11:22:33:44:55:66**
> >> +
> >> +show
> >> +----
> >> +
> >> +Displays all active measurements (device path for each) and the full
> >> +set of CS parameter values that will be used on the next **start** call.
> >> +When no measurements are active, ``none`` is shown.
> >> +
> >> +The parameter output is divided into three sections:
> >> +
> >> +- **Default Settings** — role, CS sync antenna selection, max TX power.
> >> +- **CS Config Params** — per-procedure configuration fields including
> >> +  mode type, step counts, PHY, and channel map.
> >> +- **CS Frequency Params** — procedure scheduling fields including
> >> +  duration, period, subevent lengths, and SNR control.
> >> +
> >> +:Usage: **> show**
> >> +:Example Show active session and all CS parameters:
> >> +       | **> show**
> >> +
> >> +RESOURCES
> >> +=========
> >> +
> >> +http://www.bluez.org
> >> +
> >> +REPORTING BUGS
> >> +==============
> >> +
> >> +linux-bluetooth@vger.kernel.org
> >> diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
> >> new file mode 100644
> >> index 000000000..73f484507
> >> --- /dev/null
> >> +++ b/doc/org.bluez.ChannelSounding1.rst
> >> @@ -0,0 +1,302 @@
> >> +==========================
> >> +org.bluez.ChannelSounding1
> >> +==========================
> >> +
> >> +----------------------------------------------
> >> +BlueZ D-Bus Channel Sounding API documentation
> >> +----------------------------------------------
> >> +
> >> +:Version: BlueZ
> >> +:Date: June 2026
> >> +:Manual section: 5
> >> +:Manual group: Linux System Administration
> >> +
> >> +Interface
> >> +=========
> >> +
> >> +:Service:      org.bluez
> >> +:Interface:    org.bluez.ChannelSounding1
> >> +:Object path:  [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> >> +:Used by:      **bluetoothctl(1)**, **bluetoothctl-cs(1)**
> >> +
> >> +Methods
> >> +-------
> >> +
> >> +void StartMeasurement(dict params)
> >> +``````````````````````````````````
> >> +
> >> +Starts a Channel Sounding distance measurement procedure on the connected
> >> +device. All configuration is supplied in a single ``a{sv}`` dictionary.
> >> +Any key that is omitted retains its current value in the daemon.
> >> +
> >> +The device to measure is identified by the D-Bus object path on which
> >> +this method is called
> >> +(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
> >> +Only one measurement per device object may be active at a time. Calling
> >> +**StartMeasurement** while a session is already active returns
> >> +``org.bluez.Error.InProgress``.
> >> +
> >> +Supported dictionary keys:
> >> +
> >> +:uint32 duration_secs (Default: 0):
> >> +
> >> +       Duration in seconds before the measurement is stopped
> >> +       automatically. A value of 0 disables the automatic timeout.
> >> +
> >> +:byte role (Default: 0x03):
> >> +
> >> +       CS role to use for the measurement.
> >> +
> >> +       Possible values:
> >> +
> >> +       :0x01: Initiator
> >> +       :0x02: Reflector
> >> +       :0x03: Both (Initiator and Reflector)
> >> +
> >> +:byte cs_sync_ant_sel (Default: 0xFF):
> >> +
> >> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> >> +       by the Bluetooth specification.
> >> +
> >> +:byte max_tx_power (Default: 0x14):
> >> +
> >> +       Maximum TX power in dBm, treated as a signed value. Valid
> >> +       range is -127 to +20 dBm.
> >> +
> >> +:byte config_id:
> >> +
> >> +       CS configuration identifier.
> >> +
> >> +:byte main_mode_type:
> >> +
> >> +       Main CS mode used in the procedure.
> >> +
> >> +:byte sub_mode_type:
> >> +
> >> +       Sub-mode within the main mode. Set to 0xFF when unused.
> >> +
> >> +:byte main_mode_min_steps:
> >> +
> >> +       Minimum number of CS main mode steps per CS subevent.
> >> +
> >> +:byte main_mode_max_steps:
> >> +
> >> +       Maximum number of CS main mode steps per CS subevent.
> >> +
> >> +:byte main_mode_repetition:
> >> +
> >> +       Number of times the main mode steps are repeated in a
> >> +       subevent.
> >> +
> >> +:byte mode0_steps:
> >> +
> >> +       Number of CS Mode 0 steps at the beginning of each subevent.
> >> +
> >> +:byte rtt_types:
> >> +
> >> +       Round Trip Time measurement types for the configuration.
> >> +
> >> +:byte cs_sync_phy:
> >> +
> >> +       PHY used for CS sync packets.
> >> +
> >> +       Possible values:
> >> +
> >> +       :0x01: LE 1M PHY
> >> +       :0x02: LE 2M PHY
> >> +
> >> +:array{byte} channel_map:
> >> +
> >> +       10-byte channel map bitmap. Must be exactly 10 bytes.
> >> +
> >> +:byte channel_map_repetition:
> >> +
> >> +       Number of consecutive repetitions of the channel map.
> >> +
> >> +:byte channel_selection_type:
> >> +
> >> +       Algorithm used for CS channel selection.
> >> +
> >> +:byte channel_shape:
> >> +
> >> +       Shape used in the channel selection algorithm.
> >> +
> >> +:byte channel_jump:
> >> +
> >> +       Channel jump size used in the channel selection algorithm.
> >> +
> >> +:byte companion_signal_enable:
> >> +
> >> +       Set to 1 to transmit a companion signal alongside the CS
> >> +       tone, 0 to disable.
> >> +
> >> +:uint16 max_procedure_duration:
> >> +
> >> +       Maximum duration of a single CS measurement procedure.
> >> +
> >> +:uint16 min_period_between_procedures:
> >> +
> >> +       Minimum time between consecutive CS measurement procedures.
> >> +
> >> +:uint16 max_period_between_procedures:
> >> +
> >> +       Maximum time between consecutive CS measurement procedures.
> >> +
> >> +:uint16 max_procedure_count:
> >> +
> >> +       Maximum number of CS measurement procedures to run.
> >> +       A value of 0 means no limit.
> >> +
> >> +:array{byte} min_sub_event_len:
> >> +
> >> +       Minimum CS subevent length as a 3-byte little-endian value.
> >> +       Must be exactly 3 bytes.
> >> +
> >> +:array{byte} max_sub_event_len:
> >> +
> >> +       Maximum CS subevent length as a 3-byte little-endian value.
> >> +       Must be exactly 3 bytes.
> >> +
> >> +:byte tone_antenna_config_selection:
> >> +
> >> +       Antenna configuration used for CS tone exchanges.
> >> +
> >> +:byte phy:
> >> +
> >> +       PHY used during CS procedures.
> >> +
> >> +       Possible values:
> >> +
> >> +       :0x01: LE 1M PHY
> >> +       :0x02: LE 2M PHY
> >> +
> >> +:byte tx_power_delta:
> >> +
> >> +       Difference between remote and local TX power during CS
> >> +       procedures. 0x80 indicates not applicable.
> >> +
> >> +:byte preferred_peer_antenna:
> >> +
> >> +       Preferred antenna to be used by the peer device.
> >> +
> >> +:byte snr_control_initiator:
> >> +
> >> +       SNR control setting for the initiator role.
> >> +       0xFF indicates no preference.
> >> +
> >> +:byte snr_control_reflector:
> >> +
> >> +       SNR control setting for the reflector role.
> >> +       0xFF indicates no preference.
> >> +
> >> +Possible errors:
> >> +
> >> +:org.bluez.Error.InProgress:
> >> +:org.bluez.Error.InvalidArgs:
> >> +:org.freedesktop.DBus.Error.Failed:
> >> +
> >> +Examples:
> >> +
> >> +:bluetoothctl set role then start:
> >> +       | [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
> >> +:bluetoothctl start with defaults:
> >> +       | [cs] > start [dev_addr [duration_secs]]
> >> +
> >> +void SetDefaultSettings(dict params)
> >> +`````````````````````````````````````
> >> +
> >> +Sets the CS default settings for this device without starting a
> >> +measurement. This method is intended for the Reflector role, where
> >> +the device waits passively for the remote Initiator to begin the
> >> +procedure and therefore never calls **StartMeasurement**. It allows
> >> +the application to configure ``role``, ``cs_sync_ant_sel``, and
> >> +``max_tx_power`` ahead of time so they are in effect when the
> >> +controller processes the remote CS configuration.
> >> +Need this to set default settings in Reflector role.
> >
> > There's no need for this if StartMeasurement already accepts the same
> > parameters. Each client should be capable of remembering the
> > parameters it used, so we don't bloat the daemon with them.
> >
> We have introduced the Defset option specifically for the Reflector role
> because Start Measurement is not applicable in this role.
> If users want to configure default settings while operating
> in the Reflector role, they should have the option to do so
> through the Defset option.
> >> +Supported dictionary keys:
> >> +
> >> +:byte role (Default: 0x03):
> >> +
> >> +       CS role to use for the measurement.
> >> +
> >> +       Possible values:
> >> +
> >> +       :0x01: Initiator
> >> +       :0x02: Reflector
> >> +       :0x03: Both (Initiator and Reflector)
> >> +
> >> +:byte cs_sync_ant_sel (Default: 0xFF):
> >> +
> >> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> >> +       by the Bluetooth specification.
> >> +
> >> +:byte max_tx_power (Default: 0x14):
> >> +
> >> +       Maximum TX power in dBm, treated as a signed value. Valid
> >> +       range is -127 to +20 dBm.
> >> +
> >> +Possible errors:
> >> +
> >> +:org.bluez.Error.InvalidArgs:
> >> +:org.freedesktop.DBus.Error.Failed:
> >> +
> >> +Examples:
> >> +
> >> +:bluetoothctl configure as Reflector:
> >> +       | [cs] > defset role=0x02
> >> +
> >> +void StopMeasurement(void)
> >> +``````````````````````````
> >> +
> >> +Stops the active Channel Sounding distance measurement on this device.
> >> +The device is identified by the D-Bus object path on which this method
> >> +is called — no session identifier is required.
> >> +
> >> +Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
> >> +
> >> +Possible errors:
> >> +
> >> +:org.bluez.Error.NotConnected:
> >> +:org.freedesktop.DBus.Error.Failed:
> >> +
> >> +In **bluetoothctl(1)**, the device address argument may be omitted only
> >> +when a single measurement is active; it is required when multiple
> >> +measurements are active.
> >> +
> >> +Examples:
> >> +
> >> +:bluetoothctl stop the only active measurement:
> >> +       | [cs] > stop
> >> +:bluetoothctl stop a specific device when multiple are active:
> >> +       | [cs] > stop AA:BB:CC:DD:EE:FF
> >> +
> >> +Properties
> >> +----------
> >> +
> >> +boolean Active [readonly]
> >> +`````````````````````````
> >> +
> >> +Indicates whether a CS distance measurement procedure is currently
> >> +active on this device.
> >> +
> >> +Set to ``true`` when a procedure starts — either because the local
> >> +Initiator called **StartMeasurement** successfully, or because the
> >> +remote Initiator enabled a CS procedure on the local Reflector.
> >> +
> >> +Set to ``false`` when the procedure stops for any reason: the local
> >> +application called **StopMeasurement**, the measurement duration timer
> >> +expired, or the ACL connection was dropped.
> >> +
> >> +This property emits ``PropertiesChanged`` on every transition so that
> >> +clients can track measurement state without polling.
> >
> > What about the results? Shouldn't results be announced when the
> > procedure starts?
> This is still under discussion,
> as the measurement calculations are handled by a separate daemon.

Ok, well without it this is not very useful, we only be able to
start/stop but the result won't be visible.

>
> >> +RESOURCES
> >> +=========
> >> +
> >> +http://www.bluez.org
> >> +
> >> +REPORTING BUGS
> >> +==============
> >> +
> >> +linux-bluetooth@vger.kernel.org
> >> --
> >>
> >
> >
>


-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-07 14:43       ` Luiz Augusto von Dentz
@ 2026-07-08  8:52         ` Naga Bhavani Akella
  2026-07-08 13:47           ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 12+ messages in thread
From: Naga Bhavani Akella @ 2026-07-08  8:52 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg

Hi Luiz,

On 7/7/2026 8:13 PM, Luiz Augusto von Dentz wrote:
> Hi Naga,
> 
> On Tue, Jul 7, 2026 at 3:34 AM Naga Bhavani Akella
> <naga.akella@oss.qualcomm.com> wrote:
>>
>> Hi Luiz,
>>
>> Please find the comments inline
>>
>> On 7/7/2026 2:06 AM, Luiz Augusto von Dentz wrote:
>>> Hi Naga,
>>>
>>> On Mon, Jul 6, 2026 at 12:16 PM Naga Bhavani Akella
>>> <naga.akella@oss.qualcomm.com> wrote:
>>>>
>>>> Add :Used by: field linking to bluetoothctl cs submenu and
>>>> Examples section showing corresponding bluetoothctl cs commands
>>>> for D-Bus methods
>>>> ---
>>>>  doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
>>>>  doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++
>>>
>>> These 2 needs to be splitted, each file on its own commit.
>> sure, will do that>
>>>>  2 files changed, 580 insertions(+)
>>>>  create mode 100644 doc/bluetoothctl-cs.rst
>>>>  create mode 100644 doc/org.bluez.ChannelSounding1.rst
>>>>
>>>> diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
>>>> new file mode 100644
>>>> index 000000000..c16c89298
>>>> --- /dev/null
>>>> +++ b/doc/bluetoothctl-cs.rst
>>>> @@ -0,0 +1,278 @@
>>>> +================
>>>> +bluetoothctl-cs
>>>> +================
>>>> +
>>>> +--------------------------
>>>> +Channel Sounding Submenu
>>>> +--------------------------
>>>> +
>>>> +:Version: BlueZ
>>>> +:Copyright: Free use of this software is granted under the terms of the GNU
>>>> +            Lesser General Public Licenses (LGPL).
>>>> +:Date: June 2026
>>>> +:Manual section: 1
>>>> +:Manual group: Linux System Administration
>>>> +
>>>> +SYNOPSIS
>>>> +========
>>>> +
>>>> +**bluetoothctl** [--options] [cs.commands]
>>>> +
>>>> +This submenu controls Bluetooth Channel Sounding (CS) distance measurement
>>>> +using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
>>>> +starting and stopping measurements and inspecting the current parameter
>>>> +state and active session identifier.
>>>> +
>>>> +CS parameters can be overridden on any **start** call using inline
>>>> +``param=value`` arguments. Overrides are applied to the local parameter
>>>> +state before the measurement is started, so **show** reflects them
>>>> +immediately after.
>>>> +
>>>> +
>>>> +Channel Sounding Commands
>>>> +=========================
>>>> +
>>>> +start
>>>> +-----
>>>> +
>>>> +Sets one or more CS parameters and starts a distance measurement on the
>>>> +connected device. All configuration is sent to the daemon in a single
>>>> +**StartMeasurement** call. On success the device path is printed to
>>>> +the console. Multiple simultaneous sessions across different devices are
>>>> +supported; each is tracked independently.
>>>> +
>>>> +Calling **start** on a device that already has an active measurement
>>>> +returns an error without starting a second session on the same device.
>>>> +
>>>> +Positional arguments are optional and must appear before any
>>>> +``param=value`` pairs:
>>>> +
>>>> +- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
>>>> +  no timeout.
>>>> +
>>>> +Any additional argument of the form ``param=value`` overrides the named
>>>> +parameter for this call and for all subsequent calls. Array-valued
>>>> +parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
>>>> +use colon-separated hex bytes with no ``0x`` prefix.
>>>> +
>>>> +:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
>>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
>>>> +:[dev_addr]: Bluetooth address of the target device (optional; uses the
>>>> +             only available CS-capable device when omitted)
>>>> +:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
>>>> +:[param=value]: One or more ``param=value`` overrides (optional)
>>>> +
>>>> +**Settable parameters:**
>>>> +
>>>> +.. list-table::
>>>> +   :header-rows: 1
>>>> +   :widths: 35 15 50
>>>> +
>>>> +   * - Parameter
>>>> +     - Default
>>>> +     - Description
>>>> +   * - ``role``
>>>> +     - ``0x03``
>>>> +     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
>>>> +   * - ``cs_sync_ant_sel``
>>>> +     - ``0xFF``
>>>> +     - CS sync antenna selection (0xFE/0xFF reserved)
>>>> +   * - ``max_tx_power``
>>>> +     - ``20``
>>>> +     - Max TX power in dBm (signed, range −127 to +20)
>>>> +   * - ``config_id``
>>>> +     - ``0``
>>>> +     - CS configuration identifier
>>>> +   * - ``main_mode_type``
>>>> +     - ``1``
>>>> +     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
>>>> +   * - ``sub_mode_type``
>>>> +     - ``0xFF``
>>>> +     - Sub-mode within main mode; ``0xFF`` = unused
>>>> +   * - ``main_mode_min_steps``
>>>> +     - ``2``
>>>> +     - Min CS main mode steps per subevent
>>>> +   * - ``main_mode_max_steps``
>>>> +     - ``3``
>>>> +     - Max CS main mode steps per subevent
>>>> +   * - ``main_mode_repetition``
>>>> +     - ``1``
>>>> +     - Times main mode steps are repeated in a subevent
>>>> +   * - ``mode0_steps``
>>>> +     - ``2``
>>>> +     - CS Mode 0 steps at the beginning of each subevent
>>>> +   * - ``rtt_types``
>>>> +     - ``0``
>>>> +     - RTT measurement types bitmask
>>>> +   * - ``cs_sync_phy``
>>>> +     - ``0x01``
>>>> +     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
>>>> +   * - ``channel_map``
>>>> +     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
>>>> +     - 10-byte channel map bitmap (colon-separated hex)
>>>> +   * - ``channel_map_repetition``
>>>> +     - ``1``
>>>> +     - Consecutive repetitions of the channel map
>>>> +   * - ``channel_selection_type``
>>>> +     - ``0``
>>>> +     - CS channel selection algorithm
>>>> +   * - ``channel_shape``
>>>> +     - ``0``
>>>> +     - Shape used in channel selection algorithm
>>>> +   * - ``channel_jump``
>>>> +     - ``2``
>>>> +     - Channel jump size
>>>> +   * - ``companion_signal_enable``
>>>> +     - ``0``
>>>> +     - ``1`` to transmit companion signal, ``0`` to disable
>>>> +   * - ``max_procedure_duration``
>>>> +     - ``1600``
>>>> +     - Maximum duration of one CS measurement procedure
>>>> +   * - ``min_period_between_procedures``
>>>> +     - ``30``
>>>> +     - Minimum time between consecutive procedures
>>>> +   * - ``max_period_between_procedures``
>>>> +     - ``150``
>>>> +     - Maximum time between consecutive procedures
>>>> +   * - ``max_procedure_count``
>>>> +     - ``0``
>>>> +     - Max number of procedures; ``0`` = no limit
>>>> +   * - ``min_sub_event_len``
>>>> +     - ``00:20:00``
>>>> +     - Min CS subevent length, 3-byte LE (colon-separated hex)
>>>> +   * - ``max_sub_event_len``
>>>> +     - ``03:20:00``
>>>> +     - Max CS subevent length, 3-byte LE (colon-separated hex)
>>>> +   * - ``tone_antenna_config_selection``
>>>> +     - ``0x07``
>>>> +     - Antenna config for CS tone exchanges
>>>> +   * - ``phy``
>>>> +     - ``0x01``
>>>> +     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
>>>> +   * - ``tx_power_delta``
>>>> +     - ``0x80``
>>>> +     - Remote vs local TX power delta; ``0x80`` = not applicable
>>>> +   * - ``preferred_peer_antenna``
>>>> +     - ``0x03``
>>>> +     - Preferred antenna for the peer device
>>>> +   * - ``snr_control_initiator``
>>>> +     - ``0xFF``
>>>> +     - SNR control for initiator; ``0xFF`` = no preference
>>>> +   * - ``snr_control_reflector``
>>>> +     - ``0xFF``
>>>> +     - SNR control for reflector; ``0xFF`` = no preference
>>>> +
>>>> +:Example Start with all defaults, no timeout:
>>>> +       | **> start**
>>>> +:Example Start on a specific device:
>>>> +       | **> start AA:BB:CC:DD:EE:FF**
>>>> +:Example Start on a specific device with 10-second auto-stop:
>>>> +       | **> start AA:BB:CC:DD:EE:FF 10**
>>>> +:Example Start with 10-second auto-stop (single device, address omitted):
>>>> +       | **> start 0 10**
>>>> +:Example Start with no timeout, explicit:
>>>> +       | **> start**
>>>> +:Example Start with 5-minute auto-stop:
>>>> +       | **> start AA:BB:CC:DD:EE:FF 300**
>>>> +:Example Start as Initiator only:
>>>> +       | **> start role=0x01**
>>>> +:Example Start as Reflector only:
>>>> +       | **> start role=0x02**
>>>> +:Example Start as both Initiator and Reflector:
>>>> +       | **> start role=0x03**
>>>> +:Example Start with Mode 2 (PBR) main mode:
>>>> +       | **> start main_mode_type=2**
>>>> +:Example Start with both RTT and PBR modes:
>>>> +       | **> start main_mode_type=3**
>>>> +:Example Start with LE 2M PHY for CS procedures:
>>>> +       | **> start phy=0x02**
>>>> +:Example Start with LE 2M PHY for both CS sync and procedures:
>>>> +       | **> start cs_sync_phy=0x02 phy=0x02**
>>>> +:Example Start with reduced TX power (10 dBm):
>>>> +       | **> start max_tx_power=10**
>>>> +:Example Start with companion signal enabled:
>>>> +       | **> start companion_signal_enable=1**
>>>> +:Example Start with a procedure limit of 100:
>>>> +       | **> start max_procedure_count=100**
>>>> +:Example Start with high SNR preference on both roles:
>>>> +       | **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
>>>> +:Example Start with custom channel map (all enabled):
>>>> +       | **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
>>>> +:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
>>>> +       | **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
>>>> +:Example Start both roles, Mode 1, no limit, custom step counts:
>>>> +       | **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
>>>> +:Example Start on a device, Reflector, 60-second timeout:
>>>> +       | **> start AA:BB:CC:DD:EE:FF 60 role=0x02**
>>>
>>> This approach is too complicated, and users will struggle to discover
>>> available commands without consulting the documentation. Although it
>>> offers flexibility to specify only necessary parameters for a session,
>>> this quickly becomes an individual setting abstraction similar to
>>> `defset`.
>> We already provide default values for all parameters and expose them
>> through the 'show' CS menu option, so users can easily see the
>> currently configured settings without referring to external documentation.
>> If a user wants to modify any parameter, they can do so through
>> the startmeasurement option. Additionally, only the
>> remote device address and timeout parameters are mandatory.
>> All other parameters are optional and will use their default values
>> if not explicitly specified.
>> This keeps the command usage simple while still providing the flexibility
>> to override only the settings that are relevant for a particular session.
> 
> I have to disagree that it makes the usage simple, it doesn't. It
> makes its implementation more flexible but there is no way to discover
> the parameters via tab completion, etc, The parameters are only
> available via documentation because they are not commands themselves.
> 
Sure, we can add tab completion.
Alternatively, would you prefer these to be separate commands?
Earlier, we agreed not to split these settings into individual commands,
so I followed that approach and included all the parameters in the start command.>>>
>>>> +defset
>>>> +------
>>>> +
>>>> +Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
>>>> +``max_tx_power``) on the connected device without starting a
>>>> +measurement. This is required for the Reflector role: a Reflector
>>>> +never calls **start** because it waits passively for the remote
>>>> +Initiator, so **defset** is the only way to push these settings to
>>>> +the daemon before the remote side initiates the procedure.
>>>> +
>>>> +Any ``param=value`` arguments update the local parameter state and
>>>> +are immediately sent to the daemon via **SetDefaultSettings**.
>>>
>>> I don't think this is going to work. `SetDefaultSettings` would need
>>> to remember what each client has set in case there are multiple
>>> clients, otherwise they would overwrite one another. Instead, this
>>> should be stored locally and then set as properties to
>>> StartMeasurement.
>> We have introduced the Defset option specifically for the Reflector role
>> because Start Measurement is not applicable in this role.
>> If users want to configure default settings while operating
>> in the Reflector role, they should have the option to do so
>> through the Defset option. Defset command also accepts the device path
>> as an input parameter, which allows it to distinguish between multiple clients.
> 
> Well that means SetDefaultSettings needs to work as a registration
> method, something we thought wouldn't be necessary. In that case we
> should rename it to RegisterCapabilities and the command should be
> cs.register and we will need to figure out how to decide which
> capabilities to use if multiple capabilities are registered.
> Alternatively, we could allow only one, but then we need to expose
> what the default settings are.
> 
> Anyway, the point of having each setting as a dedicated command still
> stands regardless of whether we implement a `cs.` namespace.register
> or perhaps make cs.start work as a reflector in which case it will not
> need any parameters.
> 
We can update the implementation to remove the defset option and
use cs.start for configuring the default settings parameters instead.
When the role is set to Reflector, cs.start will not initiate distance measurement,
it will only use the provided default settings.

We can also add role-specific hints in the menu so that users can easily identify
which arguments are mandatory for each role.>  >
>>>> +Omitting all arguments sends the current local values unchanged.
>>>> +
>>>> +:Usage: **> defset [param=value ...]**
>>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
>>>> +:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
>>>> +               ``max_tx_power`` (optional)
>>>> +:Example Configure as Reflector:
>>>> +       | **> defset role=0x02**
>>>> +:Example Configure as Both with reduced TX power:
>>>> +       | **> defset role=0x03 max_tx_power=10**
>>>> +:Example Apply current local values without changing them:
>>>> +       | **> defset**
>>>
>>> Like I said above, this will make it hard for users to discover what
>>> to enter without consulting the documentation. Instead, each file
>>> should probably have a dedicated command, eg `cs.role
>>> <initiator/reflector,both>`, etc for all the settings necessary for
>>> cs.start to work.
>>>
>>>> +stop
>>>> +----
>>>> +
>>>> +Stops an active CS distance measurement. When only one measurement is
>>>> +running the device address may be omitted. When multiple measurements
>>>> +are active the address is required to identify which one to stop.
>>>> +
>>>> +:Usage: **> stop [dev_addr]**
>>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
>>>> +:[dev_addr]: Bluetooth address of the device to stop (optional when
>>>> +             only one session is active; required otherwise)
>>>> +:Example Stop the only active measurement:
>>>> +       | **> stop**
>>>> +:Example Stop a specific device when multiple are active:
>>>> +       | **> stop AA:BB:CC:DD:EE:FF**
>>>> +:Example Stop a second device:
>>>> +       | **> stop 11:22:33:44:55:66**
>>>> +
>>>> +show
>>>> +----
>>>> +
>>>> +Displays all active measurements (device path for each) and the full
>>>> +set of CS parameter values that will be used on the next **start** call.
>>>> +When no measurements are active, ``none`` is shown.
>>>> +
>>>> +The parameter output is divided into three sections:
>>>> +
>>>> +- **Default Settings** — role, CS sync antenna selection, max TX power.
>>>> +- **CS Config Params** — per-procedure configuration fields including
>>>> +  mode type, step counts, PHY, and channel map.
>>>> +- **CS Frequency Params** — procedure scheduling fields including
>>>> +  duration, period, subevent lengths, and SNR control.
>>>> +
>>>> +:Usage: **> show**
>>>> +:Example Show active session and all CS parameters:
>>>> +       | **> show**
>>>> +
>>>> +RESOURCES
>>>> +=========
>>>> +
>>>> +http://www.bluez.org
>>>> +
>>>> +REPORTING BUGS
>>>> +==============
>>>> +
>>>> +linux-bluetooth@vger.kernel.org
>>>> diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
>>>> new file mode 100644
>>>> index 000000000..73f484507
>>>> --- /dev/null
>>>> +++ b/doc/org.bluez.ChannelSounding1.rst
>>>> @@ -0,0 +1,302 @@
>>>> +==========================
>>>> +org.bluez.ChannelSounding1
>>>> +==========================
>>>> +
>>>> +----------------------------------------------
>>>> +BlueZ D-Bus Channel Sounding API documentation
>>>> +----------------------------------------------
>>>> +
>>>> +:Version: BlueZ
>>>> +:Date: June 2026
>>>> +:Manual section: 5
>>>> +:Manual group: Linux System Administration
>>>> +
>>>> +Interface
>>>> +=========
>>>> +
>>>> +:Service:      org.bluez
>>>> +:Interface:    org.bluez.ChannelSounding1
>>>> +:Object path:  [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
>>>> +:Used by:      **bluetoothctl(1)**, **bluetoothctl-cs(1)**
>>>> +
>>>> +Methods
>>>> +-------
>>>> +
>>>> +void StartMeasurement(dict params)
>>>> +``````````````````````````````````
>>>> +
>>>> +Starts a Channel Sounding distance measurement procedure on the connected
>>>> +device. All configuration is supplied in a single ``a{sv}`` dictionary.
>>>> +Any key that is omitted retains its current value in the daemon.
>>>> +
>>>> +The device to measure is identified by the D-Bus object path on which
>>>> +this method is called
>>>> +(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
>>>> +Only one measurement per device object may be active at a time. Calling
>>>> +**StartMeasurement** while a session is already active returns
>>>> +``org.bluez.Error.InProgress``.
>>>> +
>>>> +Supported dictionary keys:
>>>> +
>>>> +:uint32 duration_secs (Default: 0):
>>>> +
>>>> +       Duration in seconds before the measurement is stopped
>>>> +       automatically. A value of 0 disables the automatic timeout.
>>>> +
>>>> +:byte role (Default: 0x03):
>>>> +
>>>> +       CS role to use for the measurement.
>>>> +
>>>> +       Possible values:
>>>> +
>>>> +       :0x01: Initiator
>>>> +       :0x02: Reflector
>>>> +       :0x03: Both (Initiator and Reflector)
>>>> +
>>>> +:byte cs_sync_ant_sel (Default: 0xFF):
>>>> +
>>>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
>>>> +       by the Bluetooth specification.
>>>> +
>>>> +:byte max_tx_power (Default: 0x14):
>>>> +
>>>> +       Maximum TX power in dBm, treated as a signed value. Valid
>>>> +       range is -127 to +20 dBm.
>>>> +
>>>> +:byte config_id:
>>>> +
>>>> +       CS configuration identifier.
>>>> +
>>>> +:byte main_mode_type:
>>>> +
>>>> +       Main CS mode used in the procedure.
>>>> +
>>>> +:byte sub_mode_type:
>>>> +
>>>> +       Sub-mode within the main mode. Set to 0xFF when unused.
>>>> +
>>>> +:byte main_mode_min_steps:
>>>> +
>>>> +       Minimum number of CS main mode steps per CS subevent.
>>>> +
>>>> +:byte main_mode_max_steps:
>>>> +
>>>> +       Maximum number of CS main mode steps per CS subevent.
>>>> +
>>>> +:byte main_mode_repetition:
>>>> +
>>>> +       Number of times the main mode steps are repeated in a
>>>> +       subevent.
>>>> +
>>>> +:byte mode0_steps:
>>>> +
>>>> +       Number of CS Mode 0 steps at the beginning of each subevent.
>>>> +
>>>> +:byte rtt_types:
>>>> +
>>>> +       Round Trip Time measurement types for the configuration.
>>>> +
>>>> +:byte cs_sync_phy:
>>>> +
>>>> +       PHY used for CS sync packets.
>>>> +
>>>> +       Possible values:
>>>> +
>>>> +       :0x01: LE 1M PHY
>>>> +       :0x02: LE 2M PHY
>>>> +
>>>> +:array{byte} channel_map:
>>>> +
>>>> +       10-byte channel map bitmap. Must be exactly 10 bytes.
>>>> +
>>>> +:byte channel_map_repetition:
>>>> +
>>>> +       Number of consecutive repetitions of the channel map.
>>>> +
>>>> +:byte channel_selection_type:
>>>> +
>>>> +       Algorithm used for CS channel selection.
>>>> +
>>>> +:byte channel_shape:
>>>> +
>>>> +       Shape used in the channel selection algorithm.
>>>> +
>>>> +:byte channel_jump:
>>>> +
>>>> +       Channel jump size used in the channel selection algorithm.
>>>> +
>>>> +:byte companion_signal_enable:
>>>> +
>>>> +       Set to 1 to transmit a companion signal alongside the CS
>>>> +       tone, 0 to disable.
>>>> +
>>>> +:uint16 max_procedure_duration:
>>>> +
>>>> +       Maximum duration of a single CS measurement procedure.
>>>> +
>>>> +:uint16 min_period_between_procedures:
>>>> +
>>>> +       Minimum time between consecutive CS measurement procedures.
>>>> +
>>>> +:uint16 max_period_between_procedures:
>>>> +
>>>> +       Maximum time between consecutive CS measurement procedures.
>>>> +
>>>> +:uint16 max_procedure_count:
>>>> +
>>>> +       Maximum number of CS measurement procedures to run.
>>>> +       A value of 0 means no limit.
>>>> +
>>>> +:array{byte} min_sub_event_len:
>>>> +
>>>> +       Minimum CS subevent length as a 3-byte little-endian value.
>>>> +       Must be exactly 3 bytes.
>>>> +
>>>> +:array{byte} max_sub_event_len:
>>>> +
>>>> +       Maximum CS subevent length as a 3-byte little-endian value.
>>>> +       Must be exactly 3 bytes.
>>>> +
>>>> +:byte tone_antenna_config_selection:
>>>> +
>>>> +       Antenna configuration used for CS tone exchanges.
>>>> +
>>>> +:byte phy:
>>>> +
>>>> +       PHY used during CS procedures.
>>>> +
>>>> +       Possible values:
>>>> +
>>>> +       :0x01: LE 1M PHY
>>>> +       :0x02: LE 2M PHY
>>>> +
>>>> +:byte tx_power_delta:
>>>> +
>>>> +       Difference between remote and local TX power during CS
>>>> +       procedures. 0x80 indicates not applicable.
>>>> +
>>>> +:byte preferred_peer_antenna:
>>>> +
>>>> +       Preferred antenna to be used by the peer device.
>>>> +
>>>> +:byte snr_control_initiator:
>>>> +
>>>> +       SNR control setting for the initiator role.
>>>> +       0xFF indicates no preference.
>>>> +
>>>> +:byte snr_control_reflector:
>>>> +
>>>> +       SNR control setting for the reflector role.
>>>> +       0xFF indicates no preference.
>>>> +
>>>> +Possible errors:
>>>> +
>>>> +:org.bluez.Error.InProgress:
>>>> +:org.bluez.Error.InvalidArgs:
>>>> +:org.freedesktop.DBus.Error.Failed:
>>>> +
>>>> +Examples:
>>>> +
>>>> +:bluetoothctl set role then start:
>>>> +       | [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
>>>> +:bluetoothctl start with defaults:
>>>> +       | [cs] > start [dev_addr [duration_secs]]
>>>> +
>>>> +void SetDefaultSettings(dict params)
>>>> +`````````````````````````````````````
>>>> +
>>>> +Sets the CS default settings for this device without starting a
>>>> +measurement. This method is intended for the Reflector role, where
>>>> +the device waits passively for the remote Initiator to begin the
>>>> +procedure and therefore never calls **StartMeasurement**. It allows
>>>> +the application to configure ``role``, ``cs_sync_ant_sel``, and
>>>> +``max_tx_power`` ahead of time so they are in effect when the
>>>> +controller processes the remote CS configuration.
>>>> +Need this to set default settings in Reflector role.
>>>
>>> There's no need for this if StartMeasurement already accepts the same
>>> parameters. Each client should be capable of remembering the
>>> parameters it used, so we don't bloat the daemon with them.
>>>
>> We have introduced the Defset option specifically for the Reflector role
>> because Start Measurement is not applicable in this role.
>> If users want to configure default settings while operating
>> in the Reflector role, they should have the option to do so
>> through the Defset option.
>>>> +Supported dictionary keys:
>>>> +
>>>> +:byte role (Default: 0x03):
>>>> +
>>>> +       CS role to use for the measurement.
>>>> +
>>>> +       Possible values:
>>>> +
>>>> +       :0x01: Initiator
>>>> +       :0x02: Reflector
>>>> +       :0x03: Both (Initiator and Reflector)
>>>> +
>>>> +:byte cs_sync_ant_sel (Default: 0xFF):
>>>> +
>>>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
>>>> +       by the Bluetooth specification.
>>>> +
>>>> +:byte max_tx_power (Default: 0x14):
>>>> +
>>>> +       Maximum TX power in dBm, treated as a signed value. Valid
>>>> +       range is -127 to +20 dBm.
>>>> +
>>>> +Possible errors:
>>>> +
>>>> +:org.bluez.Error.InvalidArgs:
>>>> +:org.freedesktop.DBus.Error.Failed:
>>>> +
>>>> +Examples:
>>>> +
>>>> +:bluetoothctl configure as Reflector:
>>>> +       | [cs] > defset role=0x02
>>>> +
>>>> +void StopMeasurement(void)
>>>> +``````````````````````````
>>>> +
>>>> +Stops the active Channel Sounding distance measurement on this device.
>>>> +The device is identified by the D-Bus object path on which this method
>>>> +is called — no session identifier is required.
>>>> +
>>>> +Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
>>>> +
>>>> +Possible errors:
>>>> +
>>>> +:org.bluez.Error.NotConnected:
>>>> +:org.freedesktop.DBus.Error.Failed:
>>>> +
>>>> +In **bluetoothctl(1)**, the device address argument may be omitted only
>>>> +when a single measurement is active; it is required when multiple
>>>> +measurements are active.
>>>> +
>>>> +Examples:
>>>> +
>>>> +:bluetoothctl stop the only active measurement:
>>>> +       | [cs] > stop
>>>> +:bluetoothctl stop a specific device when multiple are active:
>>>> +       | [cs] > stop AA:BB:CC:DD:EE:FF
>>>> +
>>>> +Properties
>>>> +----------
>>>> +
>>>> +boolean Active [readonly]
>>>> +`````````````````````````
>>>> +
>>>> +Indicates whether a CS distance measurement procedure is currently
>>>> +active on this device.
>>>> +
>>>> +Set to ``true`` when a procedure starts — either because the local
>>>> +Initiator called **StartMeasurement** successfully, or because the
>>>> +remote Initiator enabled a CS procedure on the local Reflector.
>>>> +
>>>> +Set to ``false`` when the procedure stops for any reason: the local
>>>> +application called **StopMeasurement**, the measurement duration timer
>>>> +expired, or the ACL connection was dropped.
>>>> +
>>>> +This property emits ``PropertiesChanged`` on every transition so that
>>>> +clients can track measurement state without polling.
>>>
>>> What about the results? Shouldn't results be announced when the
>>> procedure starts?
>> This is still under discussion,
>> as the measurement calculations are handled by a separate daemon.
> 
> Ok, well without it this is not very useful, we only be able to
> start/stop but the result won't be visible.
Agree. will add signal for announcing results in the coming patchset.> 
>>
>>>> +RESOURCES
>>>> +=========
>>>> +
>>>> +http://www.bluez.org
>>>> +
>>>> +REPORTING BUGS
>>>> +==============
>>>> +
>>>> +linux-bluetooth@vger.kernel.org
>>>> --
>>>>
>>>
>>>
>>
> 
> 


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

* Re: [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples
  2026-07-08  8:52         ` Naga Bhavani Akella
@ 2026-07-08 13:47           ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-08 13:47 UTC (permalink / raw)
  To: Naga Bhavani Akella
  Cc: linux-bluetooth, quic_mohamull, quic_hbandi, quic_anubhavg

Hi Naga,

On Wed, Jul 8, 2026 at 4:53 AM Naga Bhavani Akella
<naga.akella@oss.qualcomm.com> wrote:
>
> Hi Luiz,
>
> On 7/7/2026 8:13 PM, Luiz Augusto von Dentz wrote:
> > Hi Naga,
> >
> > On Tue, Jul 7, 2026 at 3:34 AM Naga Bhavani Akella
> > <naga.akella@oss.qualcomm.com> wrote:
> >>
> >> Hi Luiz,
> >>
> >> Please find the comments inline
> >>
> >> On 7/7/2026 2:06 AM, Luiz Augusto von Dentz wrote:
> >>> Hi Naga,
> >>>
> >>> On Mon, Jul 6, 2026 at 12:16 PM Naga Bhavani Akella
> >>> <naga.akella@oss.qualcomm.com> wrote:
> >>>>
> >>>> Add :Used by: field linking to bluetoothctl cs submenu and
> >>>> Examples section showing corresponding bluetoothctl cs commands
> >>>> for D-Bus methods
> >>>> ---
> >>>>  doc/bluetoothctl-cs.rst            | 278 ++++++++++++++++++++++++++
> >>>>  doc/org.bluez.ChannelSounding1.rst | 302 +++++++++++++++++++++++++++++
> >>>
> >>> These 2 needs to be splitted, each file on its own commit.
> >> sure, will do that>
> >>>>  2 files changed, 580 insertions(+)
> >>>>  create mode 100644 doc/bluetoothctl-cs.rst
> >>>>  create mode 100644 doc/org.bluez.ChannelSounding1.rst
> >>>>
> >>>> diff --git a/doc/bluetoothctl-cs.rst b/doc/bluetoothctl-cs.rst
> >>>> new file mode 100644
> >>>> index 000000000..c16c89298
> >>>> --- /dev/null
> >>>> +++ b/doc/bluetoothctl-cs.rst
> >>>> @@ -0,0 +1,278 @@
> >>>> +================
> >>>> +bluetoothctl-cs
> >>>> +================
> >>>> +
> >>>> +--------------------------
> >>>> +Channel Sounding Submenu
> >>>> +--------------------------
> >>>> +
> >>>> +:Version: BlueZ
> >>>> +:Copyright: Free use of this software is granted under the terms of the GNU
> >>>> +            Lesser General Public Licenses (LGPL).
> >>>> +:Date: June 2026
> >>>> +:Manual section: 1
> >>>> +:Manual group: Linux System Administration
> >>>> +
> >>>> +SYNOPSIS
> >>>> +========
> >>>> +
> >>>> +**bluetoothctl** [--options] [cs.commands]
> >>>> +
> >>>> +This submenu controls Bluetooth Channel Sounding (CS) distance measurement
> >>>> +using the **org.bluez.ChannelSounding1(5)** D-Bus interface. It allows
> >>>> +starting and stopping measurements and inspecting the current parameter
> >>>> +state and active session identifier.
> >>>> +
> >>>> +CS parameters can be overridden on any **start** call using inline
> >>>> +``param=value`` arguments. Overrides are applied to the local parameter
> >>>> +state before the measurement is started, so **show** reflects them
> >>>> +immediately after.
> >>>> +
> >>>> +
> >>>> +Channel Sounding Commands
> >>>> +=========================
> >>>> +
> >>>> +start
> >>>> +-----
> >>>> +
> >>>> +Sets one or more CS parameters and starts a distance measurement on the
> >>>> +connected device. All configuration is sent to the daemon in a single
> >>>> +**StartMeasurement** call. On success the device path is printed to
> >>>> +the console. Multiple simultaneous sessions across different devices are
> >>>> +supported; each is tracked independently.
> >>>> +
> >>>> +Calling **start** on a device that already has an active measurement
> >>>> +returns an error without starting a second session on the same device.
> >>>> +
> >>>> +Positional arguments are optional and must appear before any
> >>>> +``param=value`` pairs:
> >>>> +
> >>>> +- ``duration_secs`` — auto-stop timeout in seconds; ``0`` (default) means
> >>>> +  no timeout.
> >>>> +
> >>>> +Any additional argument of the form ``param=value`` overrides the named
> >>>> +parameter for this call and for all subsequent calls. Array-valued
> >>>> +parameters (``channel_map``, ``min_sub_event_len``, ``max_sub_event_len``)
> >>>> +use colon-separated hex bytes with no ``0x`` prefix.
> >>>> +
> >>>> +:Usage: **> start [dev_addr [duration_secs]] [param=value ...]**
> >>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StartMeasurement**
> >>>> +:[dev_addr]: Bluetooth address of the target device (optional; uses the
> >>>> +             only available CS-capable device when omitted)
> >>>> +:[duration_secs]: Seconds before auto-stop (optional, default 0 = no timeout)
> >>>> +:[param=value]: One or more ``param=value`` overrides (optional)
> >>>> +
> >>>> +**Settable parameters:**
> >>>> +
> >>>> +.. list-table::
> >>>> +   :header-rows: 1
> >>>> +   :widths: 35 15 50
> >>>> +
> >>>> +   * - Parameter
> >>>> +     - Default
> >>>> +     - Description
> >>>> +   * - ``role``
> >>>> +     - ``0x03``
> >>>> +     - ``0x01`` Initiator, ``0x02`` Reflector, ``0x03`` Both
> >>>> +   * - ``cs_sync_ant_sel``
> >>>> +     - ``0xFF``
> >>>> +     - CS sync antenna selection (0xFE/0xFF reserved)
> >>>> +   * - ``max_tx_power``
> >>>> +     - ``20``
> >>>> +     - Max TX power in dBm (signed, range −127 to +20)
> >>>> +   * - ``config_id``
> >>>> +     - ``0``
> >>>> +     - CS configuration identifier
> >>>> +   * - ``main_mode_type``
> >>>> +     - ``1``
> >>>> +     - ``1`` Mode 1 (RTT), ``2`` Mode 2 (PBR), ``3`` Both
> >>>> +   * - ``sub_mode_type``
> >>>> +     - ``0xFF``
> >>>> +     - Sub-mode within main mode; ``0xFF`` = unused
> >>>> +   * - ``main_mode_min_steps``
> >>>> +     - ``2``
> >>>> +     - Min CS main mode steps per subevent
> >>>> +   * - ``main_mode_max_steps``
> >>>> +     - ``3``
> >>>> +     - Max CS main mode steps per subevent
> >>>> +   * - ``main_mode_repetition``
> >>>> +     - ``1``
> >>>> +     - Times main mode steps are repeated in a subevent
> >>>> +   * - ``mode0_steps``
> >>>> +     - ``2``
> >>>> +     - CS Mode 0 steps at the beginning of each subevent
> >>>> +   * - ``rtt_types``
> >>>> +     - ``0``
> >>>> +     - RTT measurement types bitmask
> >>>> +   * - ``cs_sync_phy``
> >>>> +     - ``0x01``
> >>>> +     - PHY for CS sync: ``0x01`` LE 1M, ``0x02`` LE 2M
> >>>> +   * - ``channel_map``
> >>>> +     - ``FC:FF:7F:FC:FF:FF:FF:FF:FF:1F``
> >>>> +     - 10-byte channel map bitmap (colon-separated hex)
> >>>> +   * - ``channel_map_repetition``
> >>>> +     - ``1``
> >>>> +     - Consecutive repetitions of the channel map
> >>>> +   * - ``channel_selection_type``
> >>>> +     - ``0``
> >>>> +     - CS channel selection algorithm
> >>>> +   * - ``channel_shape``
> >>>> +     - ``0``
> >>>> +     - Shape used in channel selection algorithm
> >>>> +   * - ``channel_jump``
> >>>> +     - ``2``
> >>>> +     - Channel jump size
> >>>> +   * - ``companion_signal_enable``
> >>>> +     - ``0``
> >>>> +     - ``1`` to transmit companion signal, ``0`` to disable
> >>>> +   * - ``max_procedure_duration``
> >>>> +     - ``1600``
> >>>> +     - Maximum duration of one CS measurement procedure
> >>>> +   * - ``min_period_between_procedures``
> >>>> +     - ``30``
> >>>> +     - Minimum time between consecutive procedures
> >>>> +   * - ``max_period_between_procedures``
> >>>> +     - ``150``
> >>>> +     - Maximum time between consecutive procedures
> >>>> +   * - ``max_procedure_count``
> >>>> +     - ``0``
> >>>> +     - Max number of procedures; ``0`` = no limit
> >>>> +   * - ``min_sub_event_len``
> >>>> +     - ``00:20:00``
> >>>> +     - Min CS subevent length, 3-byte LE (colon-separated hex)
> >>>> +   * - ``max_sub_event_len``
> >>>> +     - ``03:20:00``
> >>>> +     - Max CS subevent length, 3-byte LE (colon-separated hex)
> >>>> +   * - ``tone_antenna_config_selection``
> >>>> +     - ``0x07``
> >>>> +     - Antenna config for CS tone exchanges
> >>>> +   * - ``phy``
> >>>> +     - ``0x01``
> >>>> +     - PHY for CS procedures: ``0x01`` LE 1M, ``0x02`` LE 2M
> >>>> +   * - ``tx_power_delta``
> >>>> +     - ``0x80``
> >>>> +     - Remote vs local TX power delta; ``0x80`` = not applicable
> >>>> +   * - ``preferred_peer_antenna``
> >>>> +     - ``0x03``
> >>>> +     - Preferred antenna for the peer device
> >>>> +   * - ``snr_control_initiator``
> >>>> +     - ``0xFF``
> >>>> +     - SNR control for initiator; ``0xFF`` = no preference
> >>>> +   * - ``snr_control_reflector``
> >>>> +     - ``0xFF``
> >>>> +     - SNR control for reflector; ``0xFF`` = no preference
> >>>> +
> >>>> +:Example Start with all defaults, no timeout:
> >>>> +       | **> start**
> >>>> +:Example Start on a specific device:
> >>>> +       | **> start AA:BB:CC:DD:EE:FF**
> >>>> +:Example Start on a specific device with 10-second auto-stop:
> >>>> +       | **> start AA:BB:CC:DD:EE:FF 10**
> >>>> +:Example Start with 10-second auto-stop (single device, address omitted):
> >>>> +       | **> start 0 10**
> >>>> +:Example Start with no timeout, explicit:
> >>>> +       | **> start**
> >>>> +:Example Start with 5-minute auto-stop:
> >>>> +       | **> start AA:BB:CC:DD:EE:FF 300**
> >>>> +:Example Start as Initiator only:
> >>>> +       | **> start role=0x01**
> >>>> +:Example Start as Reflector only:
> >>>> +       | **> start role=0x02**
> >>>> +:Example Start as both Initiator and Reflector:
> >>>> +       | **> start role=0x03**
> >>>> +:Example Start with Mode 2 (PBR) main mode:
> >>>> +       | **> start main_mode_type=2**
> >>>> +:Example Start with both RTT and PBR modes:
> >>>> +       | **> start main_mode_type=3**
> >>>> +:Example Start with LE 2M PHY for CS procedures:
> >>>> +       | **> start phy=0x02**
> >>>> +:Example Start with LE 2M PHY for both CS sync and procedures:
> >>>> +       | **> start cs_sync_phy=0x02 phy=0x02**
> >>>> +:Example Start with reduced TX power (10 dBm):
> >>>> +       | **> start max_tx_power=10**
> >>>> +:Example Start with companion signal enabled:
> >>>> +       | **> start companion_signal_enable=1**
> >>>> +:Example Start with a procedure limit of 100:
> >>>> +       | **> start max_procedure_count=100**
> >>>> +:Example Start with high SNR preference on both roles:
> >>>> +       | **> start snr_control_initiator=0x01 snr_control_reflector=0x01**
> >>>> +:Example Start with custom channel map (all enabled):
> >>>> +       | **> start channel_map=FF:FF:FF:FF:FF:FF:FF:FF:FF:FF**
> >>>> +:Example Start Initiator, Mode 2, LE 2M, 30-second timeout:
> >>>> +       | **> start AA:BB:CC:DD:EE:FF 30 role=0x01 main_mode_type=2 phy=0x02 cs_sync_phy=0x02**
> >>>> +:Example Start both roles, Mode 1, no limit, custom step counts:
> >>>> +       | **> start role=0x03 main_mode_type=1 main_mode_min_steps=4 main_mode_max_steps=8**
> >>>> +:Example Start on a device, Reflector, 60-second timeout:
> >>>> +       | **> start AA:BB:CC:DD:EE:FF 60 role=0x02**
> >>>
> >>> This approach is too complicated, and users will struggle to discover
> >>> available commands without consulting the documentation. Although it
> >>> offers flexibility to specify only necessary parameters for a session,
> >>> this quickly becomes an individual setting abstraction similar to
> >>> `defset`.
> >> We already provide default values for all parameters and expose them
> >> through the 'show' CS menu option, so users can easily see the
> >> currently configured settings without referring to external documentation.
> >> If a user wants to modify any parameter, they can do so through
> >> the startmeasurement option. Additionally, only the
> >> remote device address and timeout parameters are mandatory.
> >> All other parameters are optional and will use their default values
> >> if not explicitly specified.
> >> This keeps the command usage simple while still providing the flexibility
> >> to override only the settings that are relevant for a particular session.
> >
> > I have to disagree that it makes the usage simple, it doesn't. It
> > makes its implementation more flexible but there is no way to discover
> > the parameters via tab completion, etc, The parameters are only
> > available via documentation because they are not commands themselves.
> >
> Sure, we can add tab completion.
> Alternatively, would you prefer these to be separate commands?
> Earlier, we agreed not to split these settings into individual commands,

I don't recall saying that we should make each field an individual
command, actually, I'd rather do that since it makes tab completion a
lot easier to implement.

> so I followed that approach and included all the parameters in the start command.>>>
> >>>> +defset
> >>>> +------
> >>>> +
> >>>> +Sets the CS default settings (``role``, ``cs_sync_ant_sel``,
> >>>> +``max_tx_power``) on the connected device without starting a
> >>>> +measurement. This is required for the Reflector role: a Reflector
> >>>> +never calls **start** because it waits passively for the remote
> >>>> +Initiator, so **defset** is the only way to push these settings to
> >>>> +the daemon before the remote side initiates the procedure.
> >>>> +
> >>>> +Any ``param=value`` arguments update the local parameter state and
> >>>> +are immediately sent to the daemon via **SetDefaultSettings**.
> >>>
> >>> I don't think this is going to work. `SetDefaultSettings` would need
> >>> to remember what each client has set in case there are multiple
> >>> clients, otherwise they would overwrite one another. Instead, this
> >>> should be stored locally and then set as properties to
> >>> StartMeasurement.
> >> We have introduced the Defset option specifically for the Reflector role
> >> because Start Measurement is not applicable in this role.
> >> If users want to configure default settings while operating
> >> in the Reflector role, they should have the option to do so
> >> through the Defset option. Defset command also accepts the device path
> >> as an input parameter, which allows it to distinguish between multiple clients.
> >
> > Well that means SetDefaultSettings needs to work as a registration
> > method, something we thought wouldn't be necessary. In that case we
> > should rename it to RegisterCapabilities and the command should be
> > cs.register and we will need to figure out how to decide which
> > capabilities to use if multiple capabilities are registered.
> > Alternatively, we could allow only one, but then we need to expose
> > what the default settings are.
> >
> > Anyway, the point of having each setting as a dedicated command still
> > stands regardless of whether we implement a `cs.` namespace.register
> > or perhaps make cs.start work as a reflector in which case it will not
> > need any parameters.
> >
> We can update the implementation to remove the defset option and
> use cs.start for configuring the default settings parameters instead.
> When the role is set to Reflector, cs.start will not initiate distance measurement,
> it will only use the provided default settings.
>
> We can also add role-specific hints in the menu so that users can easily identify
> which arguments are mandatory for each role.>  >
> >>>> +Omitting all arguments sends the current local values unchanged.
> >>>> +
> >>>> +:Usage: **> defset [param=value ...]**
> >>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **SetDefaultSettings**
> >>>> +:[param=value]: One or more of ``role``, ``cs_sync_ant_sel``,
> >>>> +               ``max_tx_power`` (optional)
> >>>> +:Example Configure as Reflector:
> >>>> +       | **> defset role=0x02**
> >>>> +:Example Configure as Both with reduced TX power:
> >>>> +       | **> defset role=0x03 max_tx_power=10**
> >>>> +:Example Apply current local values without changing them:
> >>>> +       | **> defset**
> >>>
> >>> Like I said above, this will make it hard for users to discover what
> >>> to enter without consulting the documentation. Instead, each file
> >>> should probably have a dedicated command, eg `cs.role
> >>> <initiator/reflector,both>`, etc for all the settings necessary for
> >>> cs.start to work.
> >>>
> >>>> +stop
> >>>> +----
> >>>> +
> >>>> +Stops an active CS distance measurement. When only one measurement is
> >>>> +running the device address may be omitted. When multiple measurements
> >>>> +are active the address is required to identify which one to stop.
> >>>> +
> >>>> +:Usage: **> stop [dev_addr]**
> >>>> +:Uses: **org.bluez.ChannelSounding1(5)** method **StopMeasurement**
> >>>> +:[dev_addr]: Bluetooth address of the device to stop (optional when
> >>>> +             only one session is active; required otherwise)
> >>>> +:Example Stop the only active measurement:
> >>>> +       | **> stop**
> >>>> +:Example Stop a specific device when multiple are active:
> >>>> +       | **> stop AA:BB:CC:DD:EE:FF**
> >>>> +:Example Stop a second device:
> >>>> +       | **> stop 11:22:33:44:55:66**
> >>>> +
> >>>> +show
> >>>> +----
> >>>> +
> >>>> +Displays all active measurements (device path for each) and the full
> >>>> +set of CS parameter values that will be used on the next **start** call.
> >>>> +When no measurements are active, ``none`` is shown.
> >>>> +
> >>>> +The parameter output is divided into three sections:
> >>>> +
> >>>> +- **Default Settings** — role, CS sync antenna selection, max TX power.
> >>>> +- **CS Config Params** — per-procedure configuration fields including
> >>>> +  mode type, step counts, PHY, and channel map.
> >>>> +- **CS Frequency Params** — procedure scheduling fields including
> >>>> +  duration, period, subevent lengths, and SNR control.
> >>>> +
> >>>> +:Usage: **> show**
> >>>> +:Example Show active session and all CS parameters:
> >>>> +       | **> show**
> >>>> +
> >>>> +RESOURCES
> >>>> +=========
> >>>> +
> >>>> +http://www.bluez.org
> >>>> +
> >>>> +REPORTING BUGS
> >>>> +==============
> >>>> +
> >>>> +linux-bluetooth@vger.kernel.org
> >>>> diff --git a/doc/org.bluez.ChannelSounding1.rst b/doc/org.bluez.ChannelSounding1.rst
> >>>> new file mode 100644
> >>>> index 000000000..73f484507
> >>>> --- /dev/null
> >>>> +++ b/doc/org.bluez.ChannelSounding1.rst
> >>>> @@ -0,0 +1,302 @@
> >>>> +==========================
> >>>> +org.bluez.ChannelSounding1
> >>>> +==========================
> >>>> +
> >>>> +----------------------------------------------
> >>>> +BlueZ D-Bus Channel Sounding API documentation
> >>>> +----------------------------------------------
> >>>> +
> >>>> +:Version: BlueZ
> >>>> +:Date: June 2026
> >>>> +:Manual section: 5
> >>>> +:Manual group: Linux System Administration
> >>>> +
> >>>> +Interface
> >>>> +=========
> >>>> +
> >>>> +:Service:      org.bluez
> >>>> +:Interface:    org.bluez.ChannelSounding1
> >>>> +:Object path:  [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
> >>>> +:Used by:      **bluetoothctl(1)**, **bluetoothctl-cs(1)**
> >>>> +
> >>>> +Methods
> >>>> +-------
> >>>> +
> >>>> +void StartMeasurement(dict params)
> >>>> +``````````````````````````````````
> >>>> +
> >>>> +Starts a Channel Sounding distance measurement procedure on the connected
> >>>> +device. All configuration is supplied in a single ``a{sv}`` dictionary.
> >>>> +Any key that is omitted retains its current value in the daemon.
> >>>> +
> >>>> +The device to measure is identified by the D-Bus object path on which
> >>>> +this method is called
> >>>> +(``[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX``).
> >>>> +Only one measurement per device object may be active at a time. Calling
> >>>> +**StartMeasurement** while a session is already active returns
> >>>> +``org.bluez.Error.InProgress``.
> >>>> +
> >>>> +Supported dictionary keys:
> >>>> +
> >>>> +:uint32 duration_secs (Default: 0):
> >>>> +
> >>>> +       Duration in seconds before the measurement is stopped
> >>>> +       automatically. A value of 0 disables the automatic timeout.
> >>>> +
> >>>> +:byte role (Default: 0x03):
> >>>> +
> >>>> +       CS role to use for the measurement.
> >>>> +
> >>>> +       Possible values:
> >>>> +
> >>>> +       :0x01: Initiator
> >>>> +       :0x02: Reflector
> >>>> +       :0x03: Both (Initiator and Reflector)
> >>>> +
> >>>> +:byte cs_sync_ant_sel (Default: 0xFF):
> >>>> +
> >>>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> >>>> +       by the Bluetooth specification.
> >>>> +
> >>>> +:byte max_tx_power (Default: 0x14):
> >>>> +
> >>>> +       Maximum TX power in dBm, treated as a signed value. Valid
> >>>> +       range is -127 to +20 dBm.
> >>>> +
> >>>> +:byte config_id:
> >>>> +
> >>>> +       CS configuration identifier.
> >>>> +
> >>>> +:byte main_mode_type:
> >>>> +
> >>>> +       Main CS mode used in the procedure.
> >>>> +
> >>>> +:byte sub_mode_type:
> >>>> +
> >>>> +       Sub-mode within the main mode. Set to 0xFF when unused.
> >>>> +
> >>>> +:byte main_mode_min_steps:
> >>>> +
> >>>> +       Minimum number of CS main mode steps per CS subevent.
> >>>> +
> >>>> +:byte main_mode_max_steps:
> >>>> +
> >>>> +       Maximum number of CS main mode steps per CS subevent.
> >>>> +
> >>>> +:byte main_mode_repetition:
> >>>> +
> >>>> +       Number of times the main mode steps are repeated in a
> >>>> +       subevent.
> >>>> +
> >>>> +:byte mode0_steps:
> >>>> +
> >>>> +       Number of CS Mode 0 steps at the beginning of each subevent.
> >>>> +
> >>>> +:byte rtt_types:
> >>>> +
> >>>> +       Round Trip Time measurement types for the configuration.
> >>>> +
> >>>> +:byte cs_sync_phy:
> >>>> +
> >>>> +       PHY used for CS sync packets.
> >>>> +
> >>>> +       Possible values:
> >>>> +
> >>>> +       :0x01: LE 1M PHY
> >>>> +       :0x02: LE 2M PHY
> >>>> +
> >>>> +:array{byte} channel_map:
> >>>> +
> >>>> +       10-byte channel map bitmap. Must be exactly 10 bytes.
> >>>> +
> >>>> +:byte channel_map_repetition:
> >>>> +
> >>>> +       Number of consecutive repetitions of the channel map.
> >>>> +
> >>>> +:byte channel_selection_type:
> >>>> +
> >>>> +       Algorithm used for CS channel selection.
> >>>> +
> >>>> +:byte channel_shape:
> >>>> +
> >>>> +       Shape used in the channel selection algorithm.
> >>>> +
> >>>> +:byte channel_jump:
> >>>> +
> >>>> +       Channel jump size used in the channel selection algorithm.
> >>>> +
> >>>> +:byte companion_signal_enable:
> >>>> +
> >>>> +       Set to 1 to transmit a companion signal alongside the CS
> >>>> +       tone, 0 to disable.
> >>>> +
> >>>> +:uint16 max_procedure_duration:
> >>>> +
> >>>> +       Maximum duration of a single CS measurement procedure.
> >>>> +
> >>>> +:uint16 min_period_between_procedures:
> >>>> +
> >>>> +       Minimum time between consecutive CS measurement procedures.
> >>>> +
> >>>> +:uint16 max_period_between_procedures:
> >>>> +
> >>>> +       Maximum time between consecutive CS measurement procedures.
> >>>> +
> >>>> +:uint16 max_procedure_count:
> >>>> +
> >>>> +       Maximum number of CS measurement procedures to run.
> >>>> +       A value of 0 means no limit.
> >>>> +
> >>>> +:array{byte} min_sub_event_len:
> >>>> +
> >>>> +       Minimum CS subevent length as a 3-byte little-endian value.
> >>>> +       Must be exactly 3 bytes.
> >>>> +
> >>>> +:array{byte} max_sub_event_len:
> >>>> +
> >>>> +       Maximum CS subevent length as a 3-byte little-endian value.
> >>>> +       Must be exactly 3 bytes.
> >>>> +
> >>>> +:byte tone_antenna_config_selection:
> >>>> +
> >>>> +       Antenna configuration used for CS tone exchanges.
> >>>> +
> >>>> +:byte phy:
> >>>> +
> >>>> +       PHY used during CS procedures.
> >>>> +
> >>>> +       Possible values:
> >>>> +
> >>>> +       :0x01: LE 1M PHY
> >>>> +       :0x02: LE 2M PHY
> >>>> +
> >>>> +:byte tx_power_delta:
> >>>> +
> >>>> +       Difference between remote and local TX power during CS
> >>>> +       procedures. 0x80 indicates not applicable.
> >>>> +
> >>>> +:byte preferred_peer_antenna:
> >>>> +
> >>>> +       Preferred antenna to be used by the peer device.
> >>>> +
> >>>> +:byte snr_control_initiator:
> >>>> +
> >>>> +       SNR control setting for the initiator role.
> >>>> +       0xFF indicates no preference.
> >>>> +
> >>>> +:byte snr_control_reflector:
> >>>> +
> >>>> +       SNR control setting for the reflector role.
> >>>> +       0xFF indicates no preference.
> >>>> +
> >>>> +Possible errors:
> >>>> +
> >>>> +:org.bluez.Error.InProgress:
> >>>> +:org.bluez.Error.InvalidArgs:
> >>>> +:org.freedesktop.DBus.Error.Failed:
> >>>> +
> >>>> +Examples:
> >>>> +
> >>>> +:bluetoothctl set role then start:
> >>>> +       | [cs] > start AA:BB:CC:DD:EE:FF role=0x01 main_mode_type=2
> >>>> +:bluetoothctl start with defaults:
> >>>> +       | [cs] > start [dev_addr [duration_secs]]
> >>>> +
> >>>> +void SetDefaultSettings(dict params)
> >>>> +`````````````````````````````````````
> >>>> +
> >>>> +Sets the CS default settings for this device without starting a
> >>>> +measurement. This method is intended for the Reflector role, where
> >>>> +the device waits passively for the remote Initiator to begin the
> >>>> +procedure and therefore never calls **StartMeasurement**. It allows
> >>>> +the application to configure ``role``, ``cs_sync_ant_sel``, and
> >>>> +``max_tx_power`` ahead of time so they are in effect when the
> >>>> +controller processes the remote CS configuration.
> >>>> +Need this to set default settings in Reflector role.
> >>>
> >>> There's no need for this if StartMeasurement already accepts the same
> >>> parameters. Each client should be capable of remembering the
> >>> parameters it used, so we don't bloat the daemon with them.
> >>>
> >> We have introduced the Defset option specifically for the Reflector role
> >> because Start Measurement is not applicable in this role.
> >> If users want to configure default settings while operating
> >> in the Reflector role, they should have the option to do so
> >> through the Defset option.
> >>>> +Supported dictionary keys:
> >>>> +
> >>>> +:byte role (Default: 0x03):
> >>>> +
> >>>> +       CS role to use for the measurement.
> >>>> +
> >>>> +       Possible values:
> >>>> +
> >>>> +       :0x01: Initiator
> >>>> +       :0x02: Reflector
> >>>> +       :0x03: Both (Initiator and Reflector)
> >>>> +
> >>>> +:byte cs_sync_ant_sel (Default: 0xFF):
> >>>> +
> >>>> +       CS sync antenna selection. Values 0xFE and 0xFF are reserved
> >>>> +       by the Bluetooth specification.
> >>>> +
> >>>> +:byte max_tx_power (Default: 0x14):
> >>>> +
> >>>> +       Maximum TX power in dBm, treated as a signed value. Valid
> >>>> +       range is -127 to +20 dBm.
> >>>> +
> >>>> +Possible errors:
> >>>> +
> >>>> +:org.bluez.Error.InvalidArgs:
> >>>> +:org.freedesktop.DBus.Error.Failed:
> >>>> +
> >>>> +Examples:
> >>>> +
> >>>> +:bluetoothctl configure as Reflector:
> >>>> +       | [cs] > defset role=0x02
> >>>> +
> >>>> +void StopMeasurement(void)
> >>>> +``````````````````````````
> >>>> +
> >>>> +Stops the active Channel Sounding distance measurement on this device.
> >>>> +The device is identified by the D-Bus object path on which this method
> >>>> +is called — no session identifier is required.
> >>>> +
> >>>> +Raises ``org.bluez.Error.NotConnected`` if no measurement is active.
> >>>> +
> >>>> +Possible errors:
> >>>> +
> >>>> +:org.bluez.Error.NotConnected:
> >>>> +:org.freedesktop.DBus.Error.Failed:
> >>>> +
> >>>> +In **bluetoothctl(1)**, the device address argument may be omitted only
> >>>> +when a single measurement is active; it is required when multiple
> >>>> +measurements are active.
> >>>> +
> >>>> +Examples:
> >>>> +
> >>>> +:bluetoothctl stop the only active measurement:
> >>>> +       | [cs] > stop
> >>>> +:bluetoothctl stop a specific device when multiple are active:
> >>>> +       | [cs] > stop AA:BB:CC:DD:EE:FF
> >>>> +
> >>>> +Properties
> >>>> +----------
> >>>> +
> >>>> +boolean Active [readonly]
> >>>> +`````````````````````````
> >>>> +
> >>>> +Indicates whether a CS distance measurement procedure is currently
> >>>> +active on this device.
> >>>> +
> >>>> +Set to ``true`` when a procedure starts — either because the local
> >>>> +Initiator called **StartMeasurement** successfully, or because the
> >>>> +remote Initiator enabled a CS procedure on the local Reflector.
> >>>> +
> >>>> +Set to ``false`` when the procedure stops for any reason: the local
> >>>> +application called **StopMeasurement**, the measurement duration timer
> >>>> +expired, or the ACL connection was dropped.
> >>>> +
> >>>> +This property emits ``PropertiesChanged`` on every transition so that
> >>>> +clients can track measurement state without polling.
> >>>
> >>> What about the results? Shouldn't results be announced when the
> >>> procedure starts?
> >> This is still under discussion,
> >> as the measurement calculations are handled by a separate daemon.
> >
> > Ok, well without it this is not very useful, we only be able to
> > start/stop but the result won't be visible.
> Agree. will add signal for announcing results in the coming patchset.>
> >>
> >>>> +RESOURCES
> >>>> +=========
> >>>> +
> >>>> +http://www.bluez.org
> >>>> +
> >>>> +REPORTING BUGS
> >>>> +==============
> >>>> +
> >>>> +linux-bluetooth@vger.kernel.org
> >>>> --
> >>>>
> >>>
> >>>
> >>
> >
> >
>


-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2026-07-08 13:47 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 16:15 [PATCH BlueZ v1 0/5] Add D-Bus and bluetoothctl support for Channel Sounding control Naga Bhavani Akella
2026-07-06 16:15 ` [PATCH BlueZ v1 1/5] rap: Add Channel Sounding parameter types and APIs Naga Bhavani Akella
2026-07-06 17:05   ` Add D-Bus and bluetoothctl support for Channel Sounding control bluez.test.bot
2026-07-06 16:16 ` [PATCH BlueZ v1 2/5] src: Register GATT profiles for Channel Sounding reflector Naga Bhavani Akella
2026-07-06 16:16 ` [PATCH BlueZ v1 3/5] doc/org.bluez.ChannelSounding1: Add Used by reference and Examples Naga Bhavani Akella
2026-07-06 20:36   ` Luiz Augusto von Dentz
2026-07-07  7:34     ` Naga Bhavani Akella
2026-07-07 14:43       ` Luiz Augusto von Dentz
2026-07-08  8:52         ` Naga Bhavani Akella
2026-07-08 13:47           ` Luiz Augusto von Dentz
2026-07-06 16:16 ` [PATCH BlueZ v1 4/5] profiles: Add D-Bus Channel Sounding control API Naga Bhavani Akella
2026-07-06 16:16 ` [PATCH BlueZ v1 5/5] client: Add Channel Sounding shell submenu Naga Bhavani Akella

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