[PATCH BlueZ v1 02/12] emulator/btdev: Add emulation support for HIDS 1.1 SCI commands

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

Add BTDEV_TYPE_BREDRLE62 device type with support for Shorter
Connection Interval (SCI) feature from Bluetooth 6.2 spec:

 - LE Connection Rate Request (0x20a1)
 - LE Set Default Rate Parameters (0x20a2)
 - LE Read Minimum Supported Connection Interval (0x20a3)
 - LE Connection Rate Change event (0x37)
 - Shorter Connection Intervals feature bits
---
 emulator/btdev.c  | 107 ++++++++++++++++++++++++++++++++++++++++++++++
 emulator/btdev.h  |   1 +
 emulator/hciemu.c |   3 ++
 emulator/hciemu.h |   1 +
 4 files changed, 112 insertions(+)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index 3206caf5be86..68ec9698c6bc 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -7963,6 +7963,96 @@ static void set_le_60_commands(struct btdev *btdev)
 	btdev->cmds = cmd_le_6_0;
 }
 
+static int cmd_le_conn_rate(struct btdev *dev, const void *data, uint8_t len)
+{
+	const struct bt_hci_cmd_le_conn_rate *cmd = data;
+	struct bt_hci_evt_le_conn_rate_change ev;
+	struct btdev_conn *conn;
+	uint8_t status = BT_HCI_ERR_SUCCESS;
+
+	conn = queue_find(dev->conns, match_handle,
+				UINT_TO_PTR(le16_to_cpu(cmd->handle)));
+	if (!conn)
+		status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+
+	cmd_status(dev, status, BT_HCI_CMD_LE_CONN_RATE);
+
+	if (status)
+		return 0;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.status = BT_HCI_ERR_SUCCESS;
+	ev.handle = cmd->handle;
+	ev.interval = cmd->interval_min;
+	ev.subrate = cmd->subrate_min;
+	ev.latency = cpu_to_le16(0);
+	ev.cont_number = cmd->cont_num;
+	ev.supv_timeout = cmd->supv_timeout;
+
+	le_meta_event(dev, BT_HCI_EVT_LE_CONN_RATE_CHANGE, &ev, sizeof(ev));
+	if (conn->link)
+		le_meta_event(conn->link->dev, BT_HCI_EVT_LE_CONN_RATE_CHANGE,
+				&ev, sizeof(ev));
+
+	return 0;
+}
+
+static int cmd_le_set_def_rate(struct btdev *dev, const void *data, uint8_t len)
+{
+	uint8_t status = BT_HCI_ERR_SUCCESS;
+
+	cmd_complete(dev, BT_HCI_CMD_LE_SET_DEF_RATE, &status, sizeof(status));
+
+	return 0;
+}
+
+static int cmd_le_read_conn_interval(struct btdev *dev, const void *data,
+					uint8_t len)
+{
+	struct {
+		struct bt_hci_rsp_le_read_conn_interval rsp;
+		struct bt_hci_le_conn_interval_group grp;
+	} __attribute__ ((packed)) pdu;
+
+	memset(&pdu, 0, sizeof(pdu));
+	pdu.rsp.status = BT_HCI_ERR_SUCCESS;
+	pdu.rsp.num_grps = 1;
+	pdu.grp.min = cpu_to_le16(0x0008);	/* 1.000 ms */
+	pdu.grp.max = cpu_to_le16(0x0c80);	/* 400.000 ms */
+	pdu.grp.stride = cpu_to_le16(0x0001);	/* 0.125 ms */
+
+	cmd_complete(dev, BT_HCI_CMD_LE_READ_CONN_INTERVAL, &pdu, sizeof(pdu));
+
+	return 0;
+}
+
+#define CMD_LE_62 \
+	CMD(BT_HCI_CMD_LE_CONN_RATE, cmd_le_conn_rate, NULL), \
+	CMD(BT_HCI_CMD_LE_SET_DEF_RATE, cmd_le_set_def_rate, NULL), \
+	CMD(BT_HCI_CMD_LE_READ_CONN_INTERVAL, cmd_le_read_conn_interval, NULL)
+
+static const struct btdev_cmd cmd_le_6_2[] = {
+	CMD_COMMON_ALL,
+	CMD_COMMON_BREDR_LE,
+	CMD_COMMON_BREDR_20,
+	CMD_COMMON_BREDR_LE_40,
+	CMD_BREDR,
+	CMD_LE,
+	CMD_LE_50,
+	CMD_LE_52,
+	CMD_LE_60,
+	CMD_LE_62,
+	{}
+};
+
+static void set_le_62_commands(struct btdev *btdev)
+{
+	btdev->commands[48] |= BIT(5);	/* LE Connection Rate Request */
+	btdev->commands[48] |= BIT(6);	/* LE Set Default Rate Parameters */
+	btdev->commands[48] |= BIT(7);	/* LE Read Min Supported Conn Interval */
+	btdev->cmds = cmd_le_6_2;
+}
+
 static void set_le_commands(struct btdev *btdev)
 {
 	set_common_commands_all(btdev);
@@ -8037,6 +8127,12 @@ static void set_le_commands(struct btdev *btdev)
 		set_le_60_commands(btdev);
 		btdev->cmds = cmd_le_6_0;
 	}
+
+	/* Extra LE commands for >= 6.2 adapters */
+	if (btdev->type >= BTDEV_TYPE_BREDRLE62) {
+		set_le_62_commands(btdev);
+		btdev->cmds = cmd_le_6_2;
+	}
 }
 
 static void set_bredrle_commands(struct btdev *btdev)
@@ -8112,6 +8208,13 @@ static void set_bredrle_features(struct btdev *btdev)
 		btdev->le_features[7] |= BIT(7); /* LL Extended Features */
 	}
 
+	if (btdev->type >= BTDEV_TYPE_BREDRLE62) {
+		/* Shorter Connection Intervals */
+		btdev->le_features[9] |= BIT(0);
+		/* Shorter Connection Intervals (Host Support) */
+		btdev->le_features[9] |= BIT(1);
+	}
+
 	btdev->feat_page_2[0] |= 0x01;	/* CPB - Central Operation */
 	btdev->feat_page_2[0] |= 0x02;	/* CPB - Peripheral Operation */
 	btdev->feat_page_2[0] |= 0x04;	/* Synchronization Train */
@@ -8217,6 +8320,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 	case BTDEV_TYPE_BREDRLE50:
 	case BTDEV_TYPE_BREDRLE52:
 	case BTDEV_TYPE_BREDRLE60:
+	case BTDEV_TYPE_BREDRLE62:
 		btdev->crypto = bt_crypto_new();
 		if (!btdev->crypto) {
 			free(btdev);
@@ -8240,6 +8344,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 	case BTDEV_TYPE_BREDRLE50:
 	case BTDEV_TYPE_BREDRLE52:
 	case BTDEV_TYPE_BREDRLE60:
+	case BTDEV_TYPE_BREDRLE62:
 		btdev->version = 0x09;
 		set_bredrle_features(btdev);
 		set_bredrle_commands(btdev);
@@ -8933,6 +9038,7 @@ int btdev_set_msft_opcode(struct btdev *btdev, uint16_t opcode)
 	case BTDEV_TYPE_BREDRLE50:
 	case BTDEV_TYPE_BREDRLE52:
 	case BTDEV_TYPE_BREDRLE60:
+	case BTDEV_TYPE_BREDRLE62:
 		btdev->msft_opcode = opcode;
 		btdev->msft_cmds = cmd_msft;
 		return 0;
@@ -8991,6 +9097,7 @@ int btdev_set_emu_opcode(struct btdev *btdev, uint16_t opcode)
 	case BTDEV_TYPE_BREDRLE50:
 	case BTDEV_TYPE_BREDRLE52:
 	case BTDEV_TYPE_BREDRLE60:
+	case BTDEV_TYPE_BREDRLE62:
 		btdev->emu_opcode = opcode;
 		btdev->emu_cmds = cmd_emu;
 		return 0;
diff --git a/emulator/btdev.h b/emulator/btdev.h
index d473bd4b3414..145f7edaa262 100644
--- a/emulator/btdev.h
+++ b/emulator/btdev.h
@@ -53,6 +53,7 @@ enum btdev_type {
 	BTDEV_TYPE_BREDRLE50,
 	BTDEV_TYPE_BREDRLE52,
 	BTDEV_TYPE_BREDRLE60,
+	BTDEV_TYPE_BREDRLE62,
 };
 
 enum btdev_hook_type {
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index 15d806a62aa6..48ed63b413f4 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -437,6 +437,9 @@ struct hciemu *hciemu_new_num_debug(enum hciemu_type type, uint8_t num,
 	case HCIEMU_TYPE_BREDRLE60:
 		hciemu->btdev_type = BTDEV_TYPE_BREDRLE60;
 		break;
+	case HCIEMU_TYPE_BREDRLE62:
+		hciemu->btdev_type = BTDEV_TYPE_BREDRLE62;
+		break;
 	default:
 		return NULL;
 	}
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index e832d6350bb3..cdf1ebe16752 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -22,6 +22,7 @@ enum hciemu_type {
 	HCIEMU_TYPE_BREDRLE50,
 	HCIEMU_TYPE_BREDRLE52,
 	HCIEMU_TYPE_BREDRLE60,
+	HCIEMU_TYPE_BREDRLE62,
 };
 
 enum hciemu_hook_type {
-- 
2.54.0