Linux bluetooth development
 help / color / mirror / Atom feed
From: Kevin Dewald <kevin@simpleble.org>
To: linux-bluetooth@vger.kernel.org
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
	Kevin Dewald <kevin@simpleble.org>
Subject: [PATCH BlueZ 3/5] bearer: Add connection parameter properties
Date: Mon,  2 Nov 2026 23:27:47 -0800	[thread overview]
Message-ID: <20261103072749.50979-4-kevin@simpleble.org> (raw)
In-Reply-To: <20261103072749.50979-1-kevin@simpleble.org>

This implements the Interval, Latency, SupervisionTimeout and Subrate
properties of org.bluez.Bearer.LE1 with the values of the MGMT
Connection Subrate event, which the kernel sends when an LE connection
is established and whenever its parameters change.

Assisted-by: Claude:claude-opus-5.5
---
 src/adapter.c |  12 +++++
 src/bearer.c  | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/bearer.h  |   3 ++
 src/device.c  |   8 ++++
 src/device.h  |   3 ++
 5 files changed, 148 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index edd2fdaf4..5ecb3c784 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -9506,6 +9506,7 @@ static void conn_subrate_evt(uint16_t index, uint16_t length,
 	const struct mgmt_ev_conn_subrate *ev = param;
 	struct btd_adapter *adapter = user_data;
 	uint16_t interval, subrate, latency, cont_num, supv_timeout;
+	struct btd_device *device;
 	char dst[18];
 
 	if (length < sizeof(*ev)) {
@@ -9532,6 +9533,17 @@ static void conn_subrate_evt(uint16_t index, uint16_t length,
 		"cont_num 0x%04x timeout 0x%04x", adapter->dev_id, dst,
 		ev->addr.type, interval, subrate, latency, cont_num,
 		supv_timeout);
+
+	device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+							ev->addr.type);
+	if (!device)
+		return;
+
+	/* The interval is in units of 0.125 ms and the supervision timeout
+	 * in units of 10 ms.
+	 */
+	device_set_le_conn_params(device, interval * 125, latency,
+					supv_timeout * 10, subrate);
 }
 
 int adapter_set_io_capability(struct btd_adapter *adapter,
diff --git a/src/bearer.c b/src/bearer.c
index 51dd789f1..5285e0274 100644
--- a/src/bearer.c
+++ b/src/bearer.c
@@ -56,6 +56,13 @@ struct btd_bearer {
 	 * additional connect messages.
 	 */
 	DBusMessage *connect; /* connect message */
+
+	/* LE connection parameters, only valid while connected */
+	bool conn_params;
+	uint32_t interval; /* in microseconds */
+	uint16_t latency;
+	uint16_t supv_timeout; /* in milliseconds */
+	uint16_t subrate;
 };
 
 static void bearer_free_dbus_message(void *data)
@@ -304,6 +311,58 @@ static gboolean bearer_role_exists(const GDBusPropertyTable *property,
 	return btd_device_bdaddr_type_connected(bearer->device, bearer->type);
 }
 
+static gboolean bearer_get_interval(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_bearer *bearer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
+							&bearer->interval);
+
+	return TRUE;
+}
+
+static gboolean bearer_get_latency(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_bearer *bearer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+							&bearer->latency);
+
+	return TRUE;
+}
+
+static gboolean bearer_get_supv_timeout(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_bearer *bearer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+							&bearer->supv_timeout);
+
+	return TRUE;
+}
+
+static gboolean bearer_get_subrate(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_bearer *bearer = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+							&bearer->subrate);
+
+	return TRUE;
+}
+
+static gboolean bearer_conn_params_exists(const GDBusPropertyTable *property,
+								void *data)
+{
+	struct btd_bearer *bearer = data;
+
+	return bearer->conn_params;
+}
+
 static const GDBusSignalTable bearer_signals[] = {
 	{ GDBUS_SIGNAL("Disconnected",
 			GDBUS_ARGS({ "name", "s" }, { "message", "s" })) },
@@ -318,6 +377,14 @@ static const GDBusPropertyTable bearer_properties[] = {
 	{ "Connectable", "b", bearer_get_connectable, NULL,
 					bearer_connectable_exists },
 	{ "Role", "s", bearer_get_role, NULL, bearer_role_exists },
+	{ "Interval", "u", bearer_get_interval, NULL,
+					bearer_conn_params_exists },
+	{ "Latency", "q", bearer_get_latency, NULL,
+					bearer_conn_params_exists },
+	{ "SupervisionTimeout", "q", bearer_get_supv_timeout, NULL,
+					bearer_conn_params_exists },
+	{ "Subrate", "q", bearer_get_subrate, NULL,
+					bearer_conn_params_exists },
 	{}
 };
 
@@ -418,6 +485,59 @@ void btd_bearer_connectable(struct btd_bearer *bearer)
 					"Connectable");
 }
 
+static void bearer_conn_param_changed(struct btd_bearer *bearer,
+							const char *name)
+{
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), bearer->path,
+					bearer_interface(bearer->type), name);
+}
+
+void btd_bearer_conn_params(struct btd_bearer *bearer, uint32_t interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t subrate)
+{
+	bool added;
+
+	if (!bearer || !bearer->path)
+		return;
+
+	added = !bearer->conn_params;
+	bearer->conn_params = true;
+
+	if (added || bearer->interval != interval) {
+		bearer->interval = interval;
+		bearer_conn_param_changed(bearer, "Interval");
+	}
+
+	if (added || bearer->latency != latency) {
+		bearer->latency = latency;
+		bearer_conn_param_changed(bearer, "Latency");
+	}
+
+	if (added || bearer->supv_timeout != supv_timeout) {
+		bearer->supv_timeout = supv_timeout;
+		bearer_conn_param_changed(bearer, "SupervisionTimeout");
+	}
+
+	if (added || bearer->subrate != subrate) {
+		bearer->subrate = subrate;
+		bearer_conn_param_changed(bearer, "Subrate");
+	}
+}
+
+static void bearer_clear_conn_params(struct btd_bearer *bearer)
+{
+	if (!bearer->conn_params)
+		return;
+
+	bearer->conn_params = false;
+
+	bearer_conn_param_changed(bearer, "Interval");
+	bearer_conn_param_changed(bearer, "Latency");
+	bearer_conn_param_changed(bearer, "SupervisionTimeout");
+	bearer_conn_param_changed(bearer, "Subrate");
+}
+
 void btd_bearer_connected(struct btd_bearer *bearer, int err)
 {
 	DBusMessage *reply;
@@ -488,6 +608,8 @@ void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason)
 					bearer_interface(bearer->type),
 					"Role");
 
+	bearer_clear_conn_params(bearer);
+
 	switch (reason) {
 	case MGMT_DEV_DISCONN_UNKNOWN:
 		name = "org.bluez.Reason.Unknown";
diff --git a/src/bearer.h b/src/bearer.h
index 238d0a17c..26530c11a 100644
--- a/src/bearer.h
+++ b/src/bearer.h
@@ -19,5 +19,8 @@ void btd_bearer_destroy(struct btd_bearer *bearer);
 void btd_bearer_paired(struct btd_bearer *bearer);
 void btd_bearer_bonded(struct btd_bearer *bearer);
 void btd_bearer_connectable(struct btd_bearer *bearer);
+void btd_bearer_conn_params(struct btd_bearer *bearer, uint32_t interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t subrate);
 void btd_bearer_connected(struct btd_bearer *bearer, int err);
 void btd_bearer_disconnected(struct btd_bearer *bearer, uint8_t reason);
diff --git a/src/device.c b/src/device.c
index 4567188d6..137970637 100644
--- a/src/device.c
+++ b/src/device.c
@@ -8523,6 +8523,14 @@ void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
 					timeout);
 }
 
+void device_set_le_conn_params(struct btd_device *device, uint32_t interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t subrate)
+{
+	btd_bearer_conn_params(device->le, interval, latency, supv_timeout,
+								subrate);
+}
+
 void btd_device_foreach_service_data(struct btd_device *dev, bt_ad_func_t func,
 							void *data)
 {
diff --git a/src/device.h b/src/device.h
index 5ead73958..6756cb3ab 100644
--- a/src/device.h
+++ b/src/device.h
@@ -249,6 +249,9 @@ void btd_device_foreach_ad(struct btd_device *dev, bt_device_ad_func_t func,
 void btd_device_set_conn_param(struct btd_device *device, uint16_t min_interval,
 					uint16_t max_interval, uint16_t latency,
 					uint16_t timeout);
+void device_set_le_conn_params(struct btd_device *device, uint32_t interval,
+				uint16_t latency, uint16_t supv_timeout,
+				uint16_t subrate);
 void btd_device_foreach_service_data(struct btd_device *dev,
 					bt_device_ad_func_t func,
 					void *data);
-- 
2.54.0 (Apple Git-157)


  parent reply	other threads:[~2026-11-03  7:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  7:27 [PATCH BlueZ 0/5] Add LE connection parameters to org.bluez.Bearer.LE1 Kevin Dewald
2026-11-03  7:27 ` [PATCH BlueZ 1/5] doc/mgmt: Document Connection Subrate for LE connections and updates Kevin Dewald
2026-11-03  8:56   ` Add LE connection parameters to org.bluez.Bearer.LE1 bluez.test.bot
2026-11-03  7:27 ` [PATCH BlueZ 2/5] org.bluez.Bearer: Add connection parameter properties Kevin Dewald
2026-11-03  7:27 ` Kevin Dewald [this message]
2026-11-03  7:27 ` [PATCH BlueZ 4/5] client: Print LE connection parameters in info Kevin Dewald
2026-11-03  7:27 ` [PATCH BlueZ 5/5] test: functional: add bearer connection parameter tests Kevin Dewald
2026-11-03 16:30 ` [PATCH BlueZ 0/5] Add LE connection parameters to org.bluez.Bearer.LE1 patchwork-bot+bluetooth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261103072749.50979-4-kevin@simpleble.org \
    --to=kevin@simpleble.org \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox