[PATCH BlueZ v1 10/12] l2cap-tester: Add SCI mode tests for LE client connections

Luiz Augusto von Dentz <[email protected]> Fri, 24 Jul 2026 15:12:23 -0400
Newsgroups org.kernel.vger.linux-bluetooth
Message-ID <[email protected]>
From: Luiz Augusto von Dentz <[email protected]>

Add L2CAP LE client connection tests that load Connection Subrate
parameters (via MGMT_OP_LOAD_CONN_SUBRATE) before establishing the
connection, using the HOGP v1.2 recommended values for each of the
four HID SCI modes:

 - Fast: interval 1.25-5 ms, latency 0, subrate 1-4, cont_num 3
 - Default: interval 7.5-15 ms, latency 0, subrate 1-4, cont_num 0
 - Low Power: interval 7.5-15 ms, latency 100, subrate 1-4, cont_num 0
 - Full Range: interval 1.25-15 ms, latency 0, subrate 1-4, cont_num 1

Add the test_l2cap_le_62 macro (HCIEMU_TYPE_BREDRLE62) since SCI
requires a 6.2 controller. The subrate parameters are loaded with
the client's runtime bdaddr during setup_powered_client before the
outgoing LE connection is initiated.
---
 tools/l2cap-tester.c | 130 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
index 2771aee65814..88bfd71bf580 100644
--- a/tools/l2cap-tester.c
+++ b/tools/l2cap-tester.c
@@ -54,6 +54,16 @@ struct test_data {
 	struct tx_tstamp_data tx_ts;
 };
 
+struct l2cap_conn_subrate {
+	uint16_t min_interval;
+	uint16_t max_interval;
+	uint16_t subrate_min;
+	uint16_t subrate_max;
+	uint16_t max_latency;
+	uint16_t cont_num;
+	uint16_t supv_timeout;
+};
+
 struct l2cap_data {
 	uint16_t client_psm;
 	uint16_t server_psm;
@@ -112,6 +122,11 @@ struct l2cap_data {
 
 	/* Set PHY */
 	uint32_t phy;
+
+	/* Connection Subrate parameters to load before connecting.
+	 * The address is filled in with the client bdaddr at runtime.
+	 */
+	const struct l2cap_conn_subrate *conn_subrate;
 };
 
 static void print_debug(const char *str, void *user_data)
@@ -298,6 +313,9 @@ static void test_data_free(void *test_data)
 #define test_l2cap_le_52(name, data, setup, func) \
 	test_l2cap(name, HCIEMU_TYPE_BREDRLE52, data, setup, func)
 
+#define test_l2cap_le_62(name, data, setup, func) \
+	test_l2cap(name, HCIEMU_TYPE_BREDRLE62, data, setup, func)
+
 static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */
 
 static const struct l2cap_data client_connect_success_test = {
@@ -654,6 +672,74 @@ static const struct l2cap_data le_client_connect_success_test_1 = {
 	.server_psm = 0x0080,
 };
 
+/* HIDS 1.2 recommended SCI parameters (see HOGP v1.2 spec).
+ * Connection intervals are in units of 0.125 ms, supervision timeout in
+ * units of 10 ms.
+ */
+static const struct l2cap_conn_subrate conn_subrate_hids_fast = {
+	.min_interval = 0x000a,		/* 1.25 ms */
+	.max_interval = 0x0028,		/* 5 ms */
+	.subrate_min = 0x0001,
+	.subrate_max = 0x0004,
+	.max_latency = 0x0000,
+	.cont_num = 0x0003,
+	.supv_timeout = 0x0004,		/* 40 ms */
+};
+
+static const struct l2cap_conn_subrate conn_subrate_hids_default = {
+	.min_interval = 0x003c,		/* 7.5 ms */
+	.max_interval = 0x0078,		/* 15 ms */
+	.subrate_min = 0x0001,
+	.subrate_max = 0x0004,
+	.max_latency = 0x0000,
+	.cont_num = 0x0000,
+	.supv_timeout = 0x000c,		/* 120 ms */
+};
+
+static const struct l2cap_conn_subrate conn_subrate_hids_low_power = {
+	.min_interval = 0x003c,		/* 7.5 ms */
+	.max_interval = 0x0078,		/* 15 ms */
+	.subrate_min = 0x0001,
+	.subrate_max = 0x0004,
+	.max_latency = 0x0064,		/* 100 */
+	.cont_num = 0x0000,
+	.supv_timeout = 0x04bc,		/* 12120 ms */
+};
+
+static const struct l2cap_conn_subrate conn_subrate_hids_full_range = {
+	.min_interval = 0x000a,		/* 1.25 ms */
+	.max_interval = 0x0078,		/* 15 ms */
+	.subrate_min = 0x0001,
+	.subrate_max = 0x0004,
+	.max_latency = 0x0000,
+	.cont_num = 0x0001,
+	.supv_timeout = 0x000c,		/* 120 ms */
+};
+
+static const struct l2cap_data le_client_connect_subrate_fast_test = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.conn_subrate = &conn_subrate_hids_fast,
+};
+
+static const struct l2cap_data le_client_connect_subrate_default_test = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.conn_subrate = &conn_subrate_hids_default,
+};
+
+static const struct l2cap_data le_client_connect_subrate_low_power_test = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.conn_subrate = &conn_subrate_hids_low_power,
+};
+
+static const struct l2cap_data le_client_connect_subrate_full_range_test = {
+	.client_psm = 0x0080,
+	.server_psm = 0x0080,
+	.conn_subrate = &conn_subrate_hids_full_range,
+};
+
 static const struct l2cap_data le_client_connect_close_test_1 = {
 	.client_psm = 0x0080,
 };
@@ -1512,6 +1598,37 @@ static void setup_powered_client(const void *test_data)
 		mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
 				sizeof(param), param, NULL, NULL, NULL);
 
+	if (test && test->conn_subrate) {
+		const struct l2cap_conn_subrate *cs = test->conn_subrate;
+		const uint8_t *client_bdaddr;
+		struct {
+			struct mgmt_cp_load_conn_subrate cp;
+			struct mgmt_conn_subrate param;
+		} __attribute__((packed)) load;
+
+		client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+		if (!client_bdaddr) {
+			tester_setup_failed();
+			return;
+		}
+
+		memset(&load, 0, sizeof(load));
+		load.cp.param_count = cpu_to_le16(1);
+		memcpy(&load.param.addr.bdaddr, client_bdaddr, 6);
+		load.param.addr.type = BDADDR_LE_PUBLIC;
+		load.param.min_interval = cpu_to_le16(cs->min_interval);
+		load.param.max_interval = cpu_to_le16(cs->max_interval);
+		load.param.subrate_min = cpu_to_le16(cs->subrate_min);
+		load.param.subrate_max = cpu_to_le16(cs->subrate_max);
+		load.param.max_latency = cpu_to_le16(cs->max_latency);
+		load.param.cont_num = cpu_to_le16(cs->cont_num);
+		load.param.supv_timeout = cpu_to_le16(cs->supv_timeout);
+
+		mgmt_send(data->mgmt, MGMT_OP_LOAD_CONN_SUBRATE,
+				data->mgmt_index, sizeof(load), &load,
+				NULL, NULL, NULL);
+	}
+
 	mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
 			sizeof(param), param, setup_powered_client_callback,
 			NULL, NULL);
@@ -3118,6 +3235,19 @@ int main(int argc, char *argv[])
 	test_l2cap_le("L2CAP LE Client - Success",
 				&le_client_connect_success_test_1,
 				setup_powered_client, test_connect);
+
+	test_l2cap_le_62("L2CAP LE Client - SCI Fast Mode",
+				&le_client_connect_subrate_fast_test,
+				setup_powered_client, test_connect);
+	test_l2cap_le_62("L2CAP LE Client - SCI Default Mode",
+				&le_client_connect_subrate_default_test,
+				setup_powered_client, test_connect);
+	test_l2cap_le_62("L2CAP LE Client - SCI Low Power Mode",
+				&le_client_connect_subrate_low_power_test,
+				setup_powered_client, test_connect);
+	test_l2cap_le_62("L2CAP LE Client - SCI Full Range Mode",
+				&le_client_connect_subrate_full_range_test,
+				setup_powered_client, test_connect);
 	test_l2cap_le("L2CAP LE Client - Close",
 				&le_client_connect_close_test_1,
 				setup_powered_client, test_connect_close);
-- 
2.54.0