* [PATCH v1 0/6] Bluetooth: Add support for HDT and EKS
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 1/6] Bluetooth: Add initial support for HDT PHY Luiz Augusto von Dentz
` (6 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds support for High Data Throughput (HDT) and Encryption Key
Schedule (EKS), introduced in Bluetooth Core 7.0:
- The LE HDT PHY and its rates, selectable with BT_PHY on ACL and with
BT_ISO_QOS on CIS and BIS, using the v2 versions of the data length,
CIG parameters and BIG creation commands and events.
- struct bt_iso_qos_v2, which adds the rates to the ISO QoS.
- LE Enable Encryption v2, Encryption Change v3 and Encryption Key
Refresh Complete v2, which carry the MIC length and whether the key
schedule is in use.
- MIC length requirements for L2CAP, set with BT_SECURITY, rejecting LE
Credit Based and Enhanced Credit Based connections with MIC Length
Too Short when the link uses a shorter MIC.
- LE Set HDT Default Parameters, with the MIC length, packet format and
rates configurable through the MGMT system configuration TLVs.
- EKS opportunistic encryption, which encrypts a link without pairing
when both devices support it, configurable through MGMT as well.
The userspace changes are posted to linux-bluetooth as:
[PATCH BlueZ v1 0/9] Add support for HDT and EKS
which add the btmon decoding, the emulator support (btdev implements a
7.0 controller with HDT and EKS, now the default for btvirt) and the
tests covering this series.
Test coverage, using the BlueZ series above with test-runner:
- l2cap-tester: 141/141, including 30 HDT tests covering each HDT rate,
MIC Length Too Short and MIC length downgrade on re-encryption.
- iso-tester: 209/209, including 68 HDT tests covering CIS and BIS on
each HDT rate and the default MIC length.
- mgmt-tester: 522/522, including the HDT setting, the HDT default
parameters and EKS opportunistic encryption system configuration.
- smp-tester: 8/8.
- BlueZ make check-functional: 53 passed, 1 skipped, with btvirt
emulating 7.0 controllers, which covers pairing alongside EKS
opportunistic encryption.
Luiz Augusto von Dentz (6):
Bluetooth: Add initial support for HDT PHY
Bluetooth: ISO: Add new QoS v2 adding rate field
Bluetooth: Add initial support for EKS
Bluetooth: L2CAP: Add MIC length validation for HDT PHY
Bluetooth: Add HCI_OP_LE_SET_HDT_DEFAULT_PARAMS support
Bluetooth: Add EKS Opportunistic Encryption support
include/net/bluetooth/bluetooth.h | 65 +++-
include/net/bluetooth/hci.h | 217 +++++++++++-
include/net/bluetooth/hci_core.h | 60 +++-
include/net/bluetooth/hci_sync.h | 9 +-
include/net/bluetooth/l2cap.h | 2 +
include/net/bluetooth/mgmt.h | 1 +
net/bluetooth/hci_conn.c | 241 +++++++------
net/bluetooth/hci_core.c | 4 +
net/bluetooth/hci_event.c | 535 ++++++++++++++++++++++++----
net/bluetooth/hci_sync.c | 569 +++++++++++++++++++++++++++++-
net/bluetooth/iso.c | 136 ++++++-
net/bluetooth/l2cap_core.c | 54 ++-
net/bluetooth/l2cap_sock.c | 6 +-
net/bluetooth/mgmt.c | 6 +
net/bluetooth/mgmt_config.c | 49 +++
net/bluetooth/smp.c | 30 +-
net/bluetooth/smp.h | 2 +
17 files changed, 1762 insertions(+), 224 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v1 1/6] Bluetooth: Add initial support for HDT PHY
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 0/6] Bluetooth: Add support for HDT and EKS Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-04 2:25 ` [v1,1/6] " bluez.test.bot
2026-11-03 22:19 ` [PATCH v1 2/6] Bluetooth: ISO: Add new QoS v2 adding rate field Luiz Augusto von Dentz
` (5 subsequent siblings)
7 siblings, 1 reply; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add support for the LE HDT PHY introduced by High Data Throughput (HDT)
in Bluetooth 7.0:
- The HDT feature bits (Link Layer feature bits 76-83) and the
le_feature() helper to access the FeatureSet pages above page 0,
along with hdt_capable() and the per-rate capability checks.
- BT_PHY_HDT_* bits for the HDT2, HDT3, HDT4, HDT6 and HDT7.5 rates in
each direction, so userspace can select them with BT_PHY, which map
to the LE HDT PHY and the rate bits of PHY_Options in LE Set PHY.
- LE Read Maximum Data Length v2, LE Set Data Length v2, LE Read/Write
Suggested Default Data Length v2 and LE Data Length Change v2, used
instead of their v1 counterparts on HDT capable controllers so the
data length is set for each PHY. The values given to LE Write
Suggested Default Data Length are clamped to the maximum the
specification allows.
- LE Set CIG Parameters v2, LE CIS Established v3, LE Create BIG v2 and
LE Create BIG Complete v2, which carry the HDT rates, MIC length and
packet format of a CIS or BIS.
- MGMT_SETTING_HDT to report HDT support to userspace.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/bluetooth.h | 20 +-
include/net/bluetooth/hci.h | 179 +++++++++++++++-
include/net/bluetooth/hci_core.h | 32 +++
include/net/bluetooth/hci_sync.h | 5 +-
include/net/bluetooth/mgmt.h | 1 +
net/bluetooth/hci_conn.c | 195 ++++++++++-------
net/bluetooth/hci_event.c | 326 ++++++++++++++++++++++++++++
net/bluetooth/hci_sync.c | 342 +++++++++++++++++++++++++++++-
net/bluetooth/mgmt.c | 6 +
9 files changed, 1019 insertions(+), 87 deletions(-)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index cd9189244566..aab6a1ac9a3e 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -142,6 +142,16 @@ struct bt_voice {
#define BT_PHY_LE_2M_RX BIT(12)
#define BT_PHY_LE_CODED_TX BIT(13)
#define BT_PHY_LE_CODED_RX BIT(14)
+#define BT_PHY_HDT_2M_TX BIT(15)
+#define BT_PHY_HDT_2M_RX BIT(16)
+#define BT_PHY_HDT_3M_TX BIT(17)
+#define BT_PHY_HDT_3M_RX BIT(18)
+#define BT_PHY_HDT_4M_TX BIT(19)
+#define BT_PHY_HDT_4M_RX BIT(20)
+#define BT_PHY_HDT_6M_TX BIT(21)
+#define BT_PHY_HDT_6M_RX BIT(22)
+#define BT_PHY_HDT_7_5M_TX BIT(23)
+#define BT_PHY_HDT_7_5M_RX BIT(24)
#define BT_PHY_BREDR_MASK (BT_PHY_BR_1M_1SLOT | BT_PHY_BR_1M_3SLOT | \
BT_PHY_BR_1M_5SLOT | BT_PHY_EDR_2M_1SLOT | \
@@ -151,6 +161,13 @@ struct bt_voice {
#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \
BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \
BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX)
+#define BT_PHY_HDT_TX_MASK (BT_PHY_HDT_2M_TX | BT_PHY_HDT_3M_TX | \
+ BT_PHY_HDT_4M_TX | BT_PHY_HDT_6M_TX | \
+ BT_PHY_HDT_7_5M_TX)
+#define BT_PHY_HDT_RX_MASK (BT_PHY_HDT_2M_RX | BT_PHY_HDT_3M_RX | \
+ BT_PHY_HDT_4M_RX | BT_PHY_HDT_6M_RX | \
+ BT_PHY_HDT_7_5M_RX)
+#define BT_PHY_HDT_MASK (BT_PHY_HDT_TX_MASK | BT_PHY_HDT_RX_MASK)
#define BT_MODE 15
@@ -221,8 +238,9 @@ struct bt_iso_qos {
#define BT_ISO_PHY_1M BIT(0)
#define BT_ISO_PHY_2M BIT(1)
#define BT_ISO_PHY_CODED BIT(2)
+#define BT_ISO_PHY_HDT BIT(4)
#define BT_ISO_PHY_ANY (BT_ISO_PHY_1M | BT_ISO_PHY_2M | \
- BT_ISO_PHY_CODED)
+ BT_ISO_PHY_CODED | BT_ISO_PHY_HDT)
#define BT_CODEC 19
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 1c992e87534d..525344702c87 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -659,6 +659,16 @@ enum {
#define HCI_LE_SCI 0x01 /* byte 9 - Shorter Connection Intervals */
#define HCI_LE_SCI_HOST 0x02 /* byte 9 - Shorter Connection Intervals (Host) */
+/* HDT features - Page 1 */
+#define HCI_LE_EKS_FEATURE BIT(4) /* Byte 1 */
+#define HCI_LE_HDT_FEATURE BIT(5)
+#define HCI_LE_HDT_6_RX_FEATURE BIT(6)
+#define HCI_LE_HDT_6_TX_FEATURE BIT(7)
+#define HCI_LE_HDT_7_5_RX_FEATURE BIT(0) /* Byte 2 */
+#define HCI_LE_HDT_7_5_TX_FEATURE BIT(1)
+#define HCI_LE_HDT_PF_1 BIT(2)
+#define HCI_LE_HDT_LQF_FEATURE BIT(3)
+
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
#define HCI_CM_HOLD 0x0001
@@ -1891,9 +1901,10 @@ struct hci_cp_le_set_default_phy {
__u8 rx_phys;
} __packed;
-#define HCI_LE_SET_PHY_1M 0x01
-#define HCI_LE_SET_PHY_2M 0x02
-#define HCI_LE_SET_PHY_CODED 0x04
+#define HCI_LE_SET_PHY_1M BIT(0)
+#define HCI_LE_SET_PHY_2M BIT(1)
+#define HCI_LE_SET_PHY_CODED BIT(2)
+#define HCI_LE_SET_PHY_HDT BIT(4)
#define HCI_OP_LE_SET_PHY 0x2032
struct hci_cp_le_set_phy {
@@ -2216,7 +2227,7 @@ struct hci_bis {
__le16 sdu;
__le16 latency;
__u8 rtn;
- __u8 phy;
+ __u8 phys;
__u8 packing;
__u8 framing;
__u8 encryption;
@@ -2535,6 +2546,114 @@ struct hci_rp_le_read_conn_interval {
struct hci_le_conn_interval_group grps[];
} __packed;
+#define HCI_OP_LE_WRITE_DEF_DATA_LEN_V2 0x20b2
+struct hci_cp_le_write_def_data_len_v2 {
+ __le16 tx_len;
+ __le16 tx_time;
+ __u8 phys;
+} __packed;
+
+#define HCI_OP_LE_READ_DEF_DATA_LEN_V2 0x20b1
+struct hci_cp_le_read_def_data_len_v2 {
+ __u8 phys;
+} __packed;
+
+struct hci_rp_le_read_def_data_len_v2 {
+ __u8 status;
+ __le16 tx_len;
+ __le16 tx_time;
+} __packed;
+
+#define HCI_OP_LE_READ_MAX_DATA_LEN_V2 0x20b0
+struct hci_cp_le_read_max_data_len_v2 {
+ __u8 phy;
+} __packed;
+
+struct hci_rp_le_read_max_data_len_v2 {
+ __u8 status;
+ __le16 tx_len;
+ __le16 tx_time;
+ __le16 rx_len;
+ __le16 rx_time;
+ __u8 tx_win;
+ __u8 rx_win;
+ __u16 rx_packet;
+} __packed;
+
+#define HCI_OP_LE_SET_MAX_DATA_LEN_V2 0x20aa
+struct hci_cp_le_set_data_len_v2 {
+ __le16 handle;
+ __le16 tx_len;
+ __le16 tx_time;
+ __u8 phys;
+} __packed;
+
+struct hci_rp_le_set_data_len_v2 {
+ __u8 status;
+ __u16 handle;
+} __packed;
+
+#define HCI_OP_LE_SET_CIG_PARAMS_V2 0x20b3
+struct hci_cis_params_v2 {
+ __u8 cis_id;
+ __le16 c_sdu;
+ __le16 p_sdu;
+ __u8 c_phys;
+ __u8 p_phys;
+ __u8 c_rtn;
+ __u8 p_rtn;
+ __u8 c_coded_rates;
+ __u8 p_coded_rates;
+ __le16 c_hdt_rates;
+ __le16 p_hdt_rates;
+ __u8 mic_len;
+ __u8 format;
+} __packed;
+
+struct hci_cp_le_set_cig_params_v2 {
+ __u8 cig_id;
+ __u8 c_interval[3];
+ __u8 p_interval[3];
+ __u8 sca;
+ __u8 packing;
+ __u8 framing;
+ __le16 c_latency;
+ __le16 p_latency;
+ __u8 num_cis;
+ struct hci_cis_params_v2 cis[] __counted_by(num_cis);
+} __packed;
+
+struct hci_rp_le_set_cig_params_v2 {
+ __u8 status;
+ __u8 cig_id;
+ __u8 num_handles;
+ __le16 handle[];
+} __packed;
+
+#define HCI_OP_LE_CREATE_BIG_V2 0x20b6
+struct hci_bis_v2 {
+ __u8 sdu_interval[3];
+ __u16 sdu;
+ __u16 latency;
+ __u8 rtn;
+ __u8 phys;
+ __u8 packing;
+ __u8 framing;
+ __u8 encryption;
+ __u8 bcode[16];
+ __u8 coded_rates;
+ __u16 hdt_rates;
+ __u8 mic_len;
+ __u8 format;
+} __packed;
+
+struct hci_cp_le_create_big_v2 {
+ __u8 handle;
+ __u8 adv_handle;
+ __u8 num_bis;
+ struct hci_bis_v2 bis;
+} __packed;
+
/* ---- HCI Events ---- */
struct hci_ev_status {
__u8 status;
@@ -3360,6 +3479,58 @@ struct hci_evt_le_conn_rate_change {
__le16 supv_timeout;
} __packed;
+#define HCI_EVT_LE_CIS_ESTABLISHED_V3 0x3a
+struct hci_evt_le_cis_established_v3 {
+ __u8 status;
+ __le16 handle;
+ __u8 cig_sync_delay[3];
+ __u8 cis_sync_delay[3];
+ __u8 c_latency[3];
+ __u8 p_latency[3];
+ __u8 c_phy;
+ __u8 p_phy;
+ __u8 nse;
+ __u8 c_bn;
+ __u8 p_bn;
+ __u8 c_ft;
+ __u8 p_ft;
+ __le16 c_mtu;
+ __le16 p_mtu;
+ __le16 interval;
+ __u8 sub_interval[3];
+ __le16 c_sdu;
+ __le16 p_sdu;
+ __u8 c_sdu_interval[3];
+ __u8 p_sdu_interval[3];
+ __u8 framing;
+ __le16 c_rates;
+ __le16 p_rates;
+ __u8 config_id;
+ __u8 group_id;
+ __u8 encryption;
+ __u8 mic_len;
+} __packed;
+
+#define HCI_EVT_LE_CREATE_BIG_COMPLETE_V2 0x3b
+struct hci_evt_le_create_big_complete_v2 {
+ __u8 status;
+ __u8 handle;
+ __u8 sync_delay[3];
+ __u8 latency[3];
+ __u8 phy;
+ __u8 nse;
+ __u8 bn;
+ __u8 pto;
+ __u8 irc;
+ __le16 max_pdu;
+ __le16 interval;
+ __le16 rates;
+ __u8 encryption;
+ __u8 mic_len;
+ __u8 num_bis;
+ __le16 bis_handle[];
+} __packed;
+
#define HCI_EV_VENDOR 0xff
/* Internal events generated by Bluetooth stack */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index e33b158086fd..6a16bc1e775c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -424,6 +424,15 @@ struct hci_dev {
__u16 le_max_tx_time;
__u16 le_max_rx_len;
__u16 le_max_rx_time;
+ __u16 hdt_def_tx_len;
+ __u16 hdt_def_tx_time;
+ __u16 hdt_max_tx_len;
+ __u16 hdt_max_tx_time;
+ __u16 hdt_max_rx_len;
+ __u16 hdt_max_rx_time;
+ __u8 hdt_max_tx_win;
+ __u8 hdt_max_rx_win;
+ __u8 hdt_max_rx_packet;
__u8 le_max_key_size;
__u8 le_min_key_size;
__u16 discov_interleaved_timeout;
@@ -784,6 +793,7 @@ struct hci_conn {
__u8 le_rx_def_phys;
__u8 le_tx_phy;
__u8 le_rx_phy;
+ __u16 le_phy_opts;
__s8 rssi;
__s8 tx_power;
__s8 max_tx_power;
@@ -2148,6 +2158,28 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
(past_sender_enabled(dev) || past_receiver_enabled(dev))
#define ll_ext_feature_capable(dev) \
((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE)
+#define le_feature(dev, page, byte) \
+ (page ? ((dev)->le_features[(page - 1) * 24 + 8 + byte]) : \
+ ((dev)->le_features[byte]))
+#define hdt_capable(dev) \
+ (((dev)->le_features[7] & BIT(3)) || \
+ (le_feature(dev, 1, 1) & HCI_LE_HDT_FEATURE))
+#define hdt_enabled(dev) \
+ (le_enabled(dev) && hdt_capable(dev))
+#define hdt_6_rx_capable(dev) \
+ (le_feature(dev, 1, 1) & HCI_LE_HDT_6_RX_FEATURE)
+#define hdt_6_tx_capable(dev) \
+ (le_feature(dev, 1, 1) & HCI_LE_HDT_6_TX_FEATURE)
+#define hdt_7_5_rx_capable(dev) \
+ (le_feature(dev, 1, 2) & HCI_LE_HDT_7_5_RX_FEATURE)
+#define hdt_7_5_tx_capable(dev) \
+ (le_feature(dev, 1, 2) & HCI_LE_HDT_7_5_TX_FEATURE)
+#define hdt_pf1_capable(dev) \
+ (le_feature(dev, 1, 2) & HCI_LE_HDT_PF_1)
+#define hdt_tx_capable(conn) \
+ ((conn)->le_tx_def_phys & HCI_LE_SET_PHY_HDT)
+#define hdt_rx_capable(conn) \
+ ((conn)->le_rx_def_phys & HCI_LE_SET_PHY_HDT)
/* Channel sounding support */
#define le_cs_capable(dev) \
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index 59cb918d1405..a24f6a84a447 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -193,4 +193,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
int hci_le_read_remote_features(struct hci_conn *conn);
int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type);
-int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys);
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys, u16 phy_opts);
+int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id);
+int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos *qos,
+ u8 num_bis);
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index b02e915bb44c..17bdd6345d19 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -119,6 +119,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_PAST_SENDER BIT(23)
#define MGMT_SETTING_PAST_RECEIVER BIT(24)
#define MGMT_SETTING_SCI BIT(25)
+#define MGMT_SETTING_HDT BIT(26)
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b007f623b2ec..edf7be18a0cc 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1941,11 +1941,8 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
{
struct hci_dev *hdev = conn->hdev;
- struct hci_cp_le_create_big cp;
struct iso_list_data data;
- memset(&cp, 0, sizeof(cp));
-
data.big = qos->bcast.big;
data.bis = qos->bcast.bis;
data.count = 0;
@@ -1954,84 +1951,14 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
hci_conn_hash_list_state(hdev, bis_list, BIS_LINK,
BT_BOUND, &data);
- cp.handle = qos->bcast.big;
- cp.adv_handle = qos->bcast.bis;
- cp.num_bis = data.count;
- hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval);
- cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu);
- cp.bis.latency = cpu_to_le16(qos->bcast.out.latency);
- cp.bis.rtn = qos->bcast.out.rtn;
- cp.bis.phy = qos->bcast.out.phys;
- cp.bis.packing = qos->bcast.packing;
- cp.bis.framing = qos->bcast.framing;
- cp.bis.encryption = qos->bcast.encryption;
- memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
-
- return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
+ return hci_le_create_big_sync(conn, qos, data.count);
}
static int set_cig_params_sync(struct hci_dev *hdev, void *data)
{
- DEFINE_FLEX(struct hci_cp_le_set_cig_params, pdu, cis, num_cis, 0x1f);
u8 cig_id = PTR_UINT(data);
- struct hci_conn *conn;
- struct bt_iso_qos *qos;
- u8 aux_num_cis = 0;
- u8 cis_id;
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_cig(hdev, cig_id);
- if (!conn) {
- hci_dev_unlock(hdev);
- return 0;
- }
-
- qos = &conn->iso_qos;
- pdu->cig_id = cig_id;
- hci_cpu_to_le24(qos->ucast.out.interval, pdu->c_interval);
- hci_cpu_to_le24(qos->ucast.in.interval, pdu->p_interval);
- pdu->sca = qos->ucast.sca;
- pdu->packing = qos->ucast.packing;
- pdu->framing = qos->ucast.framing;
- pdu->c_latency = cpu_to_le16(qos->ucast.out.latency);
- pdu->p_latency = cpu_to_le16(qos->ucast.in.latency);
-
- /* Reprogram all CIS(s) with the same CIG, valid range are:
- * num_cis: 0x00 to 0x1F
- * cis_id: 0x00 to 0xEF
- */
- for (cis_id = 0x00; cis_id < 0xf0 &&
- aux_num_cis < pdu->num_cis; cis_id++) {
- struct hci_cis_params *cis;
-
- conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id);
- if (!conn)
- continue;
-
- qos = &conn->iso_qos;
-
- cis = &pdu->cis[aux_num_cis++];
- cis->cis_id = cis_id;
- cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu);
- cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu);
- cis->c_phys = qos->ucast.out.phys ? qos->ucast.out.phys :
- qos->ucast.in.phys;
- cis->p_phys = qos->ucast.in.phys ? qos->ucast.in.phys :
- qos->ucast.out.phys;
- cis->c_rtn = qos->ucast.out.rtn;
- cis->p_rtn = qos->ucast.in.rtn;
- }
- pdu->num_cis = aux_num_cis;
- hci_dev_unlock(hdev);
-
- if (!pdu->num_cis)
- return 0;
-
- return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS,
- struct_size(pdu, cis, pdu->num_cis),
- pdu, HCI_CMD_TIMEOUT);
+ return hci_le_set_cig_params_sync(hdev, cig_id);
}
static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
@@ -3122,6 +3049,72 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
if (conn->le_rx_def_phys & HCI_LE_SET_PHY_CODED)
phys |= BT_PHY_LE_CODED_RX;
+ if (conn->le_tx_def_phys & HCI_LE_SET_PHY_HDT) {
+ /* If le_phy_opts is set, it means user has
+ * specified specific PHYs to use rather than the
+ * default ones.
+ */
+ if (conn->le_phy_opts) {
+ if (conn->le_phy_opts & BIT(2))
+ phys |= BT_PHY_HDT_2M_TX;
+
+ if (conn->le_phy_opts & BIT(3))
+ phys |= BT_PHY_HDT_3M_TX;
+
+ if (conn->le_phy_opts & BIT(4))
+ phys |= BT_PHY_HDT_4M_TX;
+
+ if (conn->le_phy_opts & BIT(5))
+ phys |= BT_PHY_HDT_6M_TX;
+
+ if (conn->le_phy_opts & BIT(6))
+ phys |= BT_PHY_HDT_7_5M_TX;
+ } else {
+ phys |= BT_PHY_HDT_2M_TX;
+ phys |= BT_PHY_HDT_3M_TX;
+ phys |= BT_PHY_HDT_4M_TX;
+
+ if (hdt_6_tx_capable(conn->hdev))
+ phys |= BT_PHY_HDT_6M_TX;
+
+ if (hdt_7_5_tx_capable(conn->hdev))
+ phys |= BT_PHY_HDT_7_5M_TX;
+ }
+ }
+
+ if (conn->le_rx_def_phys & HCI_LE_SET_PHY_HDT) {
+ /* If le_phy_opts is set, it means user has
+ * specified specific PHYs to use rather than the
+ * default ones.
+ */
+ if (conn->le_phy_opts) {
+ if (conn->le_phy_opts & BIT(2))
+ phys |= BT_PHY_HDT_2M_RX;
+
+ if (conn->le_phy_opts & BIT(3))
+ phys |= BT_PHY_HDT_3M_RX;
+
+ if (conn->le_phy_opts & BIT(4))
+ phys |= BT_PHY_HDT_4M_RX;
+
+ if (conn->le_phy_opts & BIT(5))
+ phys |= BT_PHY_HDT_6M_RX;
+
+ if (conn->le_phy_opts & BIT(6))
+ phys |= BT_PHY_HDT_7_5M_RX;
+ } else {
+ phys |= BT_PHY_HDT_2M_RX;
+ phys |= BT_PHY_HDT_3M_RX;
+ phys |= BT_PHY_HDT_4M_RX;
+
+ if (hdt_6_rx_capable(conn->hdev))
+ phys |= BT_PHY_HDT_6M_RX;
+
+ if (hdt_7_5_rx_capable(conn->hdev))
+ phys |= BT_PHY_HDT_7_5M_RX;
+ }
+ }
+
break;
}
@@ -3175,7 +3168,7 @@ static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys)
return pkt_type;
}
-static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys)
+static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys, u16 *phy_opts)
{
if (!tx_phys || !rx_phys)
return -EINVAL;
@@ -3201,12 +3194,47 @@ static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys)
if (phys & BT_PHY_LE_CODED_RX)
*rx_phys |= HCI_LE_SET_PHY_CODED;
+ if (phys & BT_PHY_HDT_2M_TX || phys & BT_PHY_HDT_3M_TX ||
+ phys & BT_PHY_HDT_4M_TX || phys & BT_PHY_HDT_6M_TX ||
+ phys & BT_PHY_HDT_7_5M_TX) {
+ *tx_phys |= HCI_LE_SET_PHY_HDT;
+
+ if (phys & BT_PHY_HDT_2M_TX)
+ *phy_opts |= BIT(2);
+ if (phys & BT_PHY_HDT_3M_TX)
+ *phy_opts |= BIT(3);
+ if (phys & BT_PHY_HDT_4M_TX)
+ *phy_opts |= BIT(4);
+ if (phys & BT_PHY_HDT_6M_TX)
+ *phy_opts |= BIT(5);
+ if (phys & BT_PHY_HDT_7_5M_TX)
+ *phy_opts |= BIT(6);
+ }
+
+ if (phys & BT_PHY_HDT_2M_RX || phys & BT_PHY_HDT_3M_RX ||
+ phys & BT_PHY_HDT_4M_RX || phys & BT_PHY_HDT_6M_RX ||
+ phys & BT_PHY_HDT_7_5M_RX) {
+ *rx_phys |= HCI_LE_SET_PHY_HDT;
+
+ if (phys & BT_PHY_HDT_2M_RX)
+ *phy_opts |= BIT(2);
+ if (phys & BT_PHY_HDT_3M_RX)
+ *phy_opts |= BIT(3);
+ if (phys & BT_PHY_HDT_4M_RX)
+ *phy_opts |= BIT(4);
+ if (phys & BT_PHY_HDT_6M_RX)
+ *phy_opts |= BIT(5);
+ if (phys & BT_PHY_HDT_7_5M_RX)
+ *phy_opts |= BIT(6);
+ }
+
return 0;
}
int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
{
u8 tx_phys, rx_phys;
+ u16 phy_opts;
switch (conn->type) {
case SCO_LINK:
@@ -3221,13 +3249,20 @@ int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
bt_phy_pkt_type(conn, phys));
case LE_LINK:
/* Only allow setting LE PHYs if link type is LE */
- if (phys & ~BT_PHY_LE_MASK)
- return -EINVAL;
+ if (phys & ~BT_PHY_LE_MASK) {
+ /* Check if HDT PHYs are supported */
+ if (!hdt_tx_capable(conn) && !hdt_rx_capable(conn))
+ return -EINVAL;
+
+ /* Check if any HDT PHYs are set */
+ if (phys & ~BT_PHY_HDT_MASK)
+ return -EINVAL;
+ }
- if (bt_phy_le_phy(phys, &tx_phys, &rx_phys))
+ if (bt_phy_le_phy(phys, &tx_phys, &rx_phys, &phy_opts))
return -EINVAL;
- return hci_le_set_phy(conn, tx_phys, rx_phys);
+ return hci_le_set_phy(conn, tx_phys, rx_phys, phy_opts);
default:
return -EINVAL;
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index f0ce124bb12b..466f09124fcd 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2995,6 +2995,7 @@ static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status)
if (conn) {
conn->le_tx_def_phys = cp->tx_phys;
conn->le_rx_def_phys = cp->rx_phys;
+ conn->le_phy_opts = __le16_to_cpu(cp->phy_opts);
}
hci_dev_unlock(hdev);
@@ -3942,6 +3943,35 @@ static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
rcu_read_unlock();
}
+static u8 hci_cc_le_read_def_data_len_v2(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_def_data_len_v2 *rp = data;
+ struct hci_cp_le_read_def_data_len_v2 *cp;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+ if (rp->status)
+ return rp->status;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_DEF_DATA_LEN_V2);
+ if (!cp)
+ return rp->status;
+
+ switch (cp->phys) {
+ case 0x00: /* All GFSK PHYs */
+ hdev->le_def_tx_len = le16_to_cpu(rp->tx_len);
+ hdev->le_def_tx_time = le16_to_cpu(rp->tx_time);
+ break;
+ case 0x01: /* HDT PHY */
+ hdev->hdt_def_tx_len = le16_to_cpu(rp->tx_len);
+ hdev->hdt_def_tx_time = le16_to_cpu(rp->tx_time);
+ break;
+ }
+
+ return rp->status;
+}
+
static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -3969,6 +3999,78 @@ static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
return rp->status;
}
+static u8 hci_cc_le_read_max_data_len_v2(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_max_data_len_v2 *rp = data;
+ struct hci_cp_le_read_max_data_len_v2 *cp;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+ if (rp->status)
+ return rp->status;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_MAX_DATA_LEN_V2);
+ if (!cp)
+ return rp->status;
+
+ switch (cp->phy) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ hdev->le_max_tx_len = le16_to_cpu(rp->tx_len);
+ hdev->le_max_tx_time = le16_to_cpu(rp->tx_time);
+ hdev->le_max_rx_len = le16_to_cpu(rp->rx_len);
+ hdev->le_max_rx_time = le16_to_cpu(rp->rx_time);
+ break;
+ case 0x05:
+ hdev->hdt_max_tx_len = le16_to_cpu(rp->tx_len);
+ hdev->hdt_max_tx_time = le16_to_cpu(rp->tx_time);
+ hdev->hdt_max_rx_len = le16_to_cpu(rp->rx_len);
+ hdev->hdt_max_rx_time = le16_to_cpu(rp->rx_time);
+ hdev->hdt_max_tx_win = rp->tx_win;
+ hdev->hdt_max_rx_win = rp->rx_win;
+ hdev->hdt_max_rx_packet = rp->rx_packet;
+ break;
+ }
+
+ return rp->status;
+}
+
+static u8 hci_cc_le_write_def_data_len_v2(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_cp_le_write_def_data_len_v2 *sent;
+ struct hci_ev_status *rp = data;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+ if (rp->status)
+ return rp->status;
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN_V2);
+ if (!sent)
+ return rp->status;
+
+ hci_dev_lock(hdev);
+
+ switch (sent->phys) {
+ case 0x00:
+ hdev->le_def_tx_len = le16_to_cpu(sent->tx_len);
+ hdev->le_def_tx_time = le16_to_cpu(sent->tx_time);
+ break;
+ case 0x01:
+ hdev->hdt_def_tx_len = le16_to_cpu(sent->tx_len);
+ hdev->hdt_def_tx_time = le16_to_cpu(sent->tx_time);
+ break;
+ }
+
+ hci_dev_unlock(hdev);
+
+ return rp->status;
+}
+
static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)
{
struct hci_conn *conn, *tmp;
@@ -4121,6 +4223,72 @@ static u8 hci_cc_le_read_all_local_features(struct hci_dev *hdev, void *data,
return rp->status;
}
+static u8 hci_cc_le_set_cig_params_v2(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_set_cig_params_v2 *rp = data;
+ struct hci_cp_le_set_cig_params_v2 *cp;
+ struct hci_conn *conn;
+ u8 status = rp->status;
+ bool pending = false;
+ int i;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_CIG_PARAMS_V2);
+ if (!rp->status && (!cp || rp->num_handles != cp->num_cis ||
+ rp->cig_id != cp->cig_id)) {
+ bt_dev_err(hdev,
+ "unexpected Set CIG Parameters V3 response data");
+ status = HCI_ERROR_UNSPECIFIED;
+ }
+
+ hci_dev_lock(hdev);
+
+ /* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E page 2554
+ *
+ * If the Status return parameter is non-zero, then the state of the CIG
+ * and its CIS configurations shall not be changed by the command. If
+ * the CIG did not already exist, it shall not be created.
+ */
+ if (status) {
+ /* Keep current configuration, fail only the unbound CIS */
+ hci_unbound_cis_failed(hdev, rp->cig_id, status);
+ goto unlock;
+ }
+
+ /* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E page 2553
+ *
+ * If the Status return parameter is zero, then the Controller shall
+ * set the Connection_Handle arrayed return parameter to the connection
+ * handle(s) corresponding to the CIS configurations specified in
+ * the CIS_IDs command parameter, in the same order.
+ */
+ for (i = 0; i < rp->num_handles; ++i) {
+ conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, rp->cig_id,
+ cp->cis[i].cis_id);
+ if (!conn || !bacmp(&conn->dst, BDADDR_ANY))
+ continue;
+
+ if (conn->state != BT_BOUND && conn->state != BT_CONNECT)
+ continue;
+
+ if (hci_conn_set_handle(conn, __le16_to_cpu(rp->handle[i])))
+ continue;
+
+ if (conn->state == BT_CONNECT)
+ pending = true;
+ }
+
+unlock:
+ if (pending)
+ hci_le_create_cis_pending(hdev);
+
+ hci_dev_unlock(hdev);
+
+ return rp->status;
+}
+
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
{
bt_dev_dbg(hdev, "status 0x%2.2x", status);
@@ -4409,8 +4577,14 @@ static const struct hci_cc {
HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power,
sizeof(struct hci_rp_le_read_transmit_power)),
HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode),
+ HCI_CC(HCI_OP_LE_READ_DEF_DATA_LEN_V2, hci_cc_le_read_def_data_len_v2,
+ sizeof(struct hci_rp_le_read_def_data_len_v2)),
HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
sizeof(struct hci_rp_le_read_buffer_size_v2)),
+ HCI_CC(HCI_OP_LE_READ_MAX_DATA_LEN_V2, hci_cc_le_read_max_data_len_v2,
+ sizeof(struct hci_rp_le_read_max_data_len_v2)),
+ HCI_CC_STATUS(HCI_OP_LE_WRITE_DEF_DATA_LEN_V2,
+ hci_cc_le_write_def_data_len_v2),
HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_CC_PLEN),
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
@@ -4418,6 +4592,9 @@ static const struct hci_cc {
HCI_CC(HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
hci_cc_le_read_all_local_features,
sizeof(struct hci_rp_le_read_all_local_features)),
+ HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS_V2, hci_cc_le_set_cig_params_v2,
+ sizeof(struct hci_rp_le_set_cig_params_v2),
+ HCI_MAX_CC_PLEN),
};
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
@@ -7623,6 +7800,11 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED;
conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED;
}
+
+ if (!hdt_capable(conn)) {
+ conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_HDT;
+ conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_HDT;
+ }
}
if (conn->state == BT_CONFIG) {
@@ -7682,6 +7864,142 @@ static void hci_le_conn_rate_change_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
+static void hci_le_cis_established_evt_v3(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_evt_le_cis_established_v3 *ev = data;
+ struct hci_conn *conn;
+ struct bt_iso_qos *qos;
+ bool pending = false;
+ u16 handle = __le16_to_cpu(ev->handle);
+ u32 c_sdu_interval, p_sdu_interval;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, handle);
+ if (!conn) {
+ bt_dev_err(hdev,
+ "Unable to find connection with handle 0x%4.4x",
+ handle);
+ goto unlock;
+ }
+
+ if (conn->type != CIS_LINK) {
+ bt_dev_err(hdev,
+ "Invalid connection link type handle 0x%4.4x",
+ handle);
+ goto unlock;
+ }
+
+ qos = &conn->iso_qos;
+
+ pending = test_and_clear_bit(HCI_CONN_CREATE_CIS, conn->flags);
+
+ c_sdu_interval = get_unaligned_le24(ev->c_sdu_interval);
+ p_sdu_interval = get_unaligned_le24(ev->p_sdu_interval);
+
+ switch (conn->role) {
+ case HCI_ROLE_SLAVE:
+ qos->ucast.in.interval = c_sdu_interval;
+ qos->ucast.out.interval = p_sdu_interval;
+ /* Convert Transport Latency (us) to Latency (msec) */
+ qos->ucast.in.latency =
+ DIV_ROUND_CLOSEST(get_unaligned_le24(ev->c_latency),
+ 1000);
+ qos->ucast.out.latency =
+ DIV_ROUND_CLOSEST(get_unaligned_le24(ev->p_latency),
+ 1000);
+ qos->ucast.in.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0;
+ qos->ucast.out.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0;
+ qos->ucast.in.phys = le_phy_qos(ev->c_phy);
+ qos->ucast.out.phys = le_phy_qos(ev->p_phy);
+ break;
+ case HCI_ROLE_MASTER:
+ qos->ucast.in.interval = p_sdu_interval;
+ qos->ucast.out.interval = c_sdu_interval;
+ /* Convert Transport Latency (us) to Latency (msec) */
+ qos->ucast.out.latency =
+ DIV_ROUND_CLOSEST(get_unaligned_le24(ev->c_latency),
+ 1000);
+ qos->ucast.in.latency =
+ DIV_ROUND_CLOSEST(get_unaligned_le24(ev->p_latency),
+ 1000);
+ qos->ucast.out.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0;
+ qos->ucast.in.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0;
+ qos->ucast.out.phys = le_phy_qos(ev->c_phy);
+ qos->ucast.in.phys = le_phy_qos(ev->p_phy);
+ break;
+ }
+
+ if (!ev->status) {
+ conn->state = BT_CONNECTED;
+ hci_debugfs_create_conn(conn);
+ hci_conn_add_sysfs(conn);
+ hci_iso_setup_path(conn);
+ goto unlock;
+ }
+
+ conn->state = BT_CLOSED;
+ hci_connect_cfm(conn, ev->status);
+ hci_conn_del(conn);
+
+unlock:
+ if (pending)
+ hci_le_create_cis_pending(hdev);
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_le_create_big_complete_v2_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_evt_le_create_big_complete_v2 *ev = data;
+ struct hci_conn *conn;
+ __u8 i = 0;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+ if (!hci_le_ev_skb_pull(hdev, skb, HCI_EVT_LE_CREATE_BIG_COMPLETE_V2,
+ flex_array_size(ev, bis_handle, ev->num_bis)))
+ return;
+
+ hci_dev_lock(hdev);
+
+ /* Connect all BISes that are bound to the BIG */
+ while ((conn = hci_conn_hash_lookup_big_state(hdev, ev->handle,
+ BT_BOUND,
+ HCI_ROLE_MASTER))) {
+ if (ev->status) {
+ hci_connect_cfm(conn, ev->status);
+ hci_conn_del(conn);
+ continue;
+ }
+
+ if (hci_conn_set_handle(conn,
+ __le16_to_cpu(ev->bis_handle[i++])))
+ continue;
+
+ conn->state = BT_CONNECTED;
+ set_bit(HCI_CONN_BIG_CREATED, conn->flags);
+ hci_debugfs_create_conn(conn);
+ hci_conn_add_sysfs(conn);
+ hci_iso_setup_path(conn);
+ }
+
+ if (!ev->status && !i)
+ /* If no BISes have been connected for the BIG,
+ * terminate. This is in case all bound connections
+ * have been closed before the BIG creation
+ * has completed.
+ */
+ hci_cmd_sync_queue(hdev, hci_iso_term_big_sync,
+ UINT_PTR(ev->handle), NULL);
+
+ hci_dev_unlock(hdev);
+}
+
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
[_op] = { \
.func = _func, \
@@ -7799,6 +8117,14 @@ static const struct hci_le_ev {
/* [0x37 = HCI_EVT_LE_CONN_RATE_CHANGE] */
HCI_LE_EV(HCI_EVT_LE_CONN_RATE_CHANGE, hci_le_conn_rate_change_evt,
sizeof(struct hci_evt_le_conn_rate_change)),
+ /* [0x3a = HCI_EVT_LE_CIS_ESTABLISHED_V3] */
+ HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED_V3, hci_le_cis_established_evt_v3,
+ sizeof(struct hci_evt_le_cis_established_v3)),
+ /* [0x3b = HCI_EVT_LE_CREATE_BIG_COMPLETE_V2] */
+ HCI_LE_EV_VL(HCI_EVT_LE_CREATE_BIG_COMPLETE_V2,
+ hci_le_create_big_complete_v2_evt,
+ sizeof(struct hci_evt_le_create_big_complete_v2),
+ HCI_MAX_LE_SUBEVT_PLEN),
};
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 9213e3ef9f26..a076ff131bc4 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -4717,6 +4717,13 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
if (le_sci_capable(hdev))
events[6] |= 0x40; /* LE Connection Rate Change event */
+ if (hdt_capable(hdev)) {
+ /* HDT events */
+ events[7] |= BIT(0); /* LE Data Length Change v2 */
+ events[7] |= BIT(1); /* LE CIS Established v3 */
+ events[7] |= BIT(2); /* LE Create BIG Complete v2 */
+ }
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK,
sizeof(events), events, HCI_CMD_TIMEOUT);
}
@@ -4794,22 +4801,71 @@ static int hci_le_set_rpa_timeout_sync(struct hci_dev *hdev)
HCI_CMD_TIMEOUT);
}
+/* Read LE Maximum Data Length V2 */
+static int hci_le_read_max_data_len_v2_sync(struct hci_dev *hdev, u8 phy)
+{
+ struct hci_cp_le_read_max_data_len_v2 cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.phy = phy;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_MAX_DATA_LEN_V2,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
/* Read LE Maximum Data Length */
static int hci_le_read_max_data_len_sync(struct hci_dev *hdev)
{
if (!(hdev->le_features[0] & HCI_LE_DATA_LEN_EXT))
return 0;
+ /* Check if HDT PHY and HCI_OP_LE_READ_MAX_DATA_LEN_V2 are supported */
+ if (hdt_capable(hdev) && hdev->commands[50] & BIT(5)) {
+ int err;
+
+ /* Read max data length for non-hdt PHYs (use 0x01 for LE 1M) */
+ err = hci_le_read_max_data_len_v2_sync(hdev, 0x01);
+ if (err)
+ return err;
+
+ /* Read max data length for HDT (0x05) */
+ return hci_le_read_max_data_len_v2_sync(hdev, 0x05);
+ }
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL,
HCI_CMD_TIMEOUT);
}
+/* Read LE Suggested Default Data Length V2 */
+static int hci_le_read_def_data_len_v2_sync(struct hci_dev *hdev, u8 phys)
+{
+ struct hci_cp_le_read_def_data_len_v2 cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.phys = phys;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_DEF_DATA_LEN_V2,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
/* Read LE Suggested Default Data Length */
static int hci_le_read_def_data_len_sync(struct hci_dev *hdev)
{
if (!(hdev->le_features[0] & HCI_LE_DATA_LEN_EXT))
return 0;
+ if (hdt_capable(hdev) && hdev->commands[50] & BIT(6)) {
+ int err;
+
+ /* All GFSK PHYs */
+ err = hci_le_read_def_data_len_v2_sync(hdev, 0x00);
+ if (err)
+ return err;
+
+ /* HDT PHY */
+ return hci_le_read_def_data_len_v2_sync(hdev, 0x01);
+ }
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_DEF_DATA_LEN, 0, NULL,
HCI_CMD_TIMEOUT);
}
@@ -5233,6 +5289,39 @@ static const struct hci_init_stage hci_init4[] = {
{}
};
+/* Write Suggested Default Data Length V2 */
+static int hci_le_write_def_data_len_v2_sync(struct hci_dev *hdev, u8 phys)
+{
+ struct hci_cp_le_write_def_data_len_v2 cp;
+
+ switch (phys) {
+ case 0x00:
+ /* If already set to max skip */
+ if (hdev->le_def_tx_len == hdev->le_max_tx_len)
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+ /* All GFSK PHYs */
+ cp.tx_len = cpu_to_le16(hdev->le_max_tx_len);
+ cp.tx_time = cpu_to_le16(hdev->le_max_tx_time);
+ break;
+ case 0x01:
+ /* If already set to max skip */
+ if (hdev->hdt_def_tx_len == hdev->hdt_max_tx_len)
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+ /* HDT PHY */
+ cp.phys = 0x01;
+ cp.tx_len = cpu_to_le16(hdev->hdt_max_tx_len);
+ cp.tx_time = cpu_to_le16(hdev->hdt_max_tx_time);
+ break;
+ }
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN_V2,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
/* Set Suggested Default Data Length to maximum if supported */
static int hci_le_set_write_def_data_len_sync(struct hci_dev *hdev)
{
@@ -5241,7 +5330,31 @@ static int hci_le_set_write_def_data_len_sync(struct hci_dev *hdev)
if (!(hdev->le_features[0] & HCI_LE_DATA_LEN_EXT))
return 0;
+ /* Clamp if reported value is over maximum allowed by spec */
+ if (hdev->le_max_tx_len > 0x00fb)
+ hdev->le_max_tx_len = 0x00fb;
+
+ if (hdev->le_max_tx_time > 0x4290)
+ hdev->le_max_tx_time = 0x4290;
+
+ /* If controller is HDT check if it support V2 then use it */
+ if (hdt_capable(hdev) && hdev->commands[50] & BIT(7)) {
+ int err;
+
+ /* All GFSK PHYs */
+ err = hci_le_write_def_data_len_v2_sync(hdev, 0x00);
+ if (err)
+ return err;
+
+ return hci_le_write_def_data_len_v2_sync(hdev, 0x01);
+ }
+
+ /* If already set to max skip */
+ if (hdev->le_def_tx_len == hdev->le_max_tx_len)
+ return 0;
+
memset(&cp, 0, sizeof(cp));
+
cp.tx_len = cpu_to_le16(hdev->le_max_tx_len);
cp.tx_time = cpu_to_le16(hdev->le_max_tx_time);
@@ -5282,6 +5395,12 @@ static int hci_le_set_default_phy_sync(struct hci_dev *hdev)
cp.rx_phys |= HCI_LE_SET_PHY_CODED;
}
+ /* Enables HDT PHY if supported */
+ if (hdt_capable(hdev)) {
+ cp.tx_phys |= HCI_LE_SET_PHY_HDT;
+ cp.rx_phys |= HCI_LE_SET_PHY_HDT;
+ }
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEFAULT_PHY,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
@@ -8225,7 +8344,7 @@ static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data)
HCI_CMD_TIMEOUT, NULL);
}
-int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys, u16 phy_opts)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_set_phy *cp;
@@ -8239,6 +8358,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
cp->handle = cpu_to_le16(conn->handle);
cp->tx_phys = tx_phys;
cp->rx_phys = rx_phys;
+ cp->phy_opts = cpu_to_le16(phy_opts);
err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
le_phy_update_complete);
@@ -8247,3 +8367,223 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
return (err == -EEXIST) ? 0 : err;
}
+
+/* LE Set CIG Params V3 */
+static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
+{
+ DEFINE_FLEX(struct hci_cp_le_set_cig_params_v2, pdu, cis, num_cis,
+ 0x1f);
+ struct hci_conn *conn;
+ struct bt_iso_qos *qos;
+ u8 aux_num_cis = 0;
+ u8 cis_id;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_cig(hdev, cig_id);
+ if (!conn) {
+ hci_dev_unlock(hdev);
+ return 0;
+ }
+
+ qos = &conn->iso_qos;
+ pdu->cig_id = cig_id;
+ hci_cpu_to_le24(qos->ucast.out.interval, pdu->c_interval);
+ hci_cpu_to_le24(qos->ucast.in.interval, pdu->p_interval);
+ pdu->sca = qos->ucast.sca;
+ pdu->packing = qos->ucast.packing;
+ pdu->framing = qos->ucast.framing;
+ pdu->c_latency = cpu_to_le16(qos->ucast.out.latency);
+ pdu->p_latency = cpu_to_le16(qos->ucast.in.latency);
+
+ /* Reprogram all CIS(s) with the same CIG, valid range are:
+ * num_cis: 0x00 to 0x1F
+ * cis_id: 0x00 to 0xEF
+ */
+ for (cis_id = 0x00; cis_id < 0xf0 &&
+ aux_num_cis < pdu->num_cis; cis_id++) {
+ struct hci_cis_params_v2 *cis;
+
+ conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id);
+ if (!conn)
+ continue;
+
+ qos = &conn->iso_qos;
+
+ cis = &pdu->cis[aux_num_cis++];
+ cis->cis_id = cis_id;
+ cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu);
+ cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu);
+ cis->c_phys = qos->ucast.out.phys ? qos->ucast.out.phys :
+ qos->ucast.in.phys;
+ cis->p_phys = qos->ucast.in.phys ? qos->ucast.in.phys :
+ qos->ucast.out.phys;
+ cis->c_rtn = qos->ucast.out.rtn;
+ cis->p_rtn = qos->ucast.in.rtn;
+
+ if (cis->c_phys & BIT(4)) {
+ cis->c_hdt_rates |= BIT(0); /* HDT 2M */
+ cis->c_hdt_rates |= BIT(1); /* HDT 3M */
+ cis->c_hdt_rates |= BIT(2); /* HDT 4M */
+
+ if (hdt_6_tx_capable(hdev))
+ cis->c_hdt_rates |= BIT(3); /* HDT 6M */
+
+ if (hdt_7_5_tx_capable(hdev))
+ cis->c_hdt_rates |= BIT(4); /* HDT 7.5M */
+ }
+
+ if (cis->p_phys & BIT(4)) {
+ cis->p_hdt_rates |= BIT(0); /* HDT 2M */
+ cis->p_hdt_rates |= BIT(1); /* HDT 3M */
+ cis->p_hdt_rates |= BIT(2); /* HDT 4M */
+
+ if (hdt_6_rx_capable(hdev))
+ cis->p_hdt_rates |= BIT(3); /* HDT 6M */
+
+ if (hdt_7_5_rx_capable(hdev))
+ cis->p_hdt_rates |= BIT(4); /* HDT 7.5M */
+ }
+ }
+ pdu->num_cis = aux_num_cis;
+
+ hci_dev_unlock(hdev);
+
+ if (!pdu->num_cis)
+ return 0;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS_V2,
+ struct_size(pdu, cis, pdu->num_cis),
+ pdu, HCI_CMD_TIMEOUT);
+}
+
+/* LE Set CIG Params */
+int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id)
+{
+ DEFINE_FLEX(struct hci_cp_le_set_cig_params, pdu, cis, num_cis, 0x1f);
+ struct hci_conn *conn;
+ struct bt_iso_qos *qos;
+ u8 aux_num_cis = 0;
+ u8 cis_id;
+
+ if (hdt_capable(hdev) && hdev->commands[51] & BIT(0))
+ return hci_le_set_cig_params_v2_sync(hdev, cig_id);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_cig(hdev, cig_id);
+ if (!conn) {
+ hci_dev_unlock(hdev);
+ return 0;
+ }
+
+ qos = &conn->iso_qos;
+ pdu->cig_id = cig_id;
+ hci_cpu_to_le24(qos->ucast.out.interval, pdu->c_interval);
+ hci_cpu_to_le24(qos->ucast.in.interval, pdu->p_interval);
+ pdu->sca = qos->ucast.sca;
+ pdu->packing = qos->ucast.packing;
+ pdu->framing = qos->ucast.framing;
+ pdu->c_latency = cpu_to_le16(qos->ucast.out.latency);
+ pdu->p_latency = cpu_to_le16(qos->ucast.in.latency);
+
+ /* Reprogram all CIS(s) with the same CIG, valid range are:
+ * num_cis: 0x00 to 0x1F
+ * cis_id: 0x00 to 0xEF
+ */
+ for (cis_id = 0x00; cis_id < 0xf0 &&
+ aux_num_cis < pdu->num_cis; cis_id++) {
+ struct hci_cis_params *cis;
+
+ conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id);
+ if (!conn)
+ continue;
+
+ qos = &conn->iso_qos;
+
+ cis = &pdu->cis[aux_num_cis++];
+ cis->cis_id = cis_id;
+ cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu);
+ cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu);
+ cis->c_phys = qos->ucast.out.phys ? qos->ucast.out.phys :
+ qos->ucast.in.phys;
+ cis->p_phys = qos->ucast.in.phys ? qos->ucast.in.phys :
+ qos->ucast.out.phys;
+ cis->c_rtn = qos->ucast.out.rtn;
+ cis->p_rtn = qos->ucast.in.rtn;
+ }
+ pdu->num_cis = aux_num_cis;
+
+ hci_dev_unlock(hdev);
+
+ if (!pdu->num_cis)
+ return 0;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS,
+ struct_size(pdu, cis, pdu->num_cis),
+ pdu, HCI_CMD_TIMEOUT);
+}
+
+static int hci_le_create_big_v2_sync(struct hci_conn *conn,
+ struct bt_iso_qos *qos, u8 num_bis)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_create_big_v2 cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = qos->bcast.big;
+ cp.adv_handle = qos->bcast.bis;
+ cp.num_bis = num_bis;
+ hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval);
+ cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu);
+ cp.bis.latency = cpu_to_le16(qos->bcast.out.latency);
+ cp.bis.rtn = qos->bcast.out.rtn;
+ cp.bis.phys = qos->bcast.out.phys;
+ cp.bis.packing = qos->bcast.packing;
+ cp.bis.framing = qos->bcast.framing;
+ cp.bis.encryption = qos->bcast.encryption;
+ memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
+
+ /* Check if HDT PHY is set then enable HDT supported rates */
+ if (cp.bis.phys & BIT(4)) {
+ cp.bis.coded_rates |= BIT(0); /* HDT 2M */
+ cp.bis.coded_rates |= BIT(1); /* HDT 3M */
+ cp.bis.coded_rates |= BIT(2); /* HDT 4M */
+
+ if (hdt_6_tx_capable(hdev))
+ cp.bis.coded_rates |= BIT(3); /* HDT 6M */
+
+ if (hdt_7_5_tx_capable(hdev))
+ cp.bis.coded_rates |= BIT(4); /* HDT 7.5M */
+ }
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_BIG_V2,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
+int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos *qos,
+ u8 num_bis)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_create_big cp;
+
+ if (hdt_capable(hdev) && hdev->commands[51] & BIT(3))
+ return hci_le_create_big_v2_sync(conn, qos, num_bis);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = qos->bcast.big;
+ cp.adv_handle = qos->bcast.bis;
+ cp.num_bis = num_bis;
+ hci_cpu_to_le24(qos->bcast.out.interval, cp.bis.sdu_interval);
+ cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu);
+ cp.bis.latency = cpu_to_le16(qos->bcast.out.latency);
+ cp.bis.rtn = qos->bcast.out.rtn;
+ cp.bis.phys = qos->bcast.out.phys;
+ cp.bis.packing = qos->bcast.packing;
+ cp.bis.framing = qos->bcast.framing;
+ cp.bis.encryption = qos->bcast.encryption;
+ memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_BIG,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 260ef2a45719..6ed3bf6f1738 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -865,6 +865,9 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (le_sci_capable(hdev))
settings |= MGMT_SETTING_SCI;
+ if (hdt_capable(hdev))
+ settings |= MGMT_SETTING_HDT;
+
settings |= MGMT_SETTING_PHY_CONFIGURATION;
return settings;
@@ -959,6 +962,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (le_sci_enabled(hdev))
settings |= MGMT_SETTING_SCI;
+ if (hdt_enabled(hdev))
+ settings |= MGMT_SETTING_HDT;
+
return settings;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* RE: [v1,1/6] Bluetooth: Add initial support for HDT PHY
2026-11-03 22:19 ` [PATCH v1 1/6] Bluetooth: Add initial support for HDT PHY Luiz Augusto von Dentz
@ 2026-11-04 2:25 ` bluez.test.bot
0 siblings, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-11-04 2:25 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
[-- Attachment #1: Type: text/plain, Size: 6062 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/series/1200957/
---Test result---
Test Summary:
CheckPatch PASS 3.53 seconds
VerifyFixes PASS 0.07 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.27 seconds
SubjectPrefix PASS 0.07 seconds
BuildKernel PASS 32.35 seconds
CheckAllWarning PASS 38.39 seconds
CheckSparse WARNING 40.71 seconds
BuildKernel32 PASS 32.46 seconds
CheckKernelLLVM PASS 34.66 seconds
TestRunnerSetup PASS 765.96 seconds
TestRunner_l2cap-tester FAIL 10.79 seconds
TestRunner_iso-tester PASS 11.13 seconds
TestRunner_bnep-tester PASS 3.17 seconds
TestRunner_mgmt-tester PASS 89.20 seconds
TestRunner_rfcomm-tester PASS 4.58 seconds
TestRunner_sco-tester PASS 5.75 seconds
TestRunner_ioctl-tester PASS 7.83 seconds
TestRunner_mesh-tester PASS 5.57 seconds
TestRunner_smp-tester PASS 4.48 seconds
TestRunner_userchan-tester PASS 3.29 seconds
TestRunner_6lowpan-tester PASS 5.09 seconds
IncrementalBuild PASS 30.61 seconds
Details
##############################
Test: CheckSparse - WARNING
Desc: Run sparse tool with linux kernel
Output:
net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):net/bluetooth/hci_sync.c:8425:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8425:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8425:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8426:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8426:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8426:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8427:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8427:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8427:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8430:50: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8430:50: left side has type restricted __le16net/bluetooth/hci_sync.c:8430:50: right side has type unsigned longnet/bluetooth/hci_sync.c:8433:50: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8433:50: left side has type restricted __le16net/bluetooth/hci_sync.c:8433:50: right side has type unsigned longnet/bluetooth/hci_sync.c:8437:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8437:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8437:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8438:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8438:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8438:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8439:42: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8439:42: left side has type restricted __le16net/bluetooth/hci_sync.c:8439:42: right side has type unsigned longnet/bluetooth/hci_sync.c:8442:50: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8442:50: left side has type restricted __le16net/bluetooth/hci_sync.c:8442:50: right side has type unsigned longnet/bluetooth/hci_sync.c:8445:50: warning: invalid assignment: |=net/bluetooth/hci_sync.c:8445:50: left side has type restricted __le16net/bluetooth/hci_sync.c:8445:50: right side has type unsigned longnet/bluetooth/hci_sync.c:8538:20: warning: incorrect type in assignment (different base types)net/bluetooth/hci_sync.c:8538:20: expected unsigned short [addressable] [assigned] [usertype] sdunet/bluetooth/hci_sync.c:8538:20: got restricted __le16 [usertype]net/bluetooth/hci_sync.c:8539:24: warning: incorrect type in assignment (different base types)net/bluetooth/hci_sync.c:8539:24: expected unsigned short [addressable] [assigned] [usertype] latencynet/bluetooth/hci_sync.c:8539:24: got restricted __le16 [usertype]
##############################
Test: TestRunner_l2cap-tester - FAIL
Desc: Run l2cap-tester with test-runner
Output:
Sanitizer errors:
==35==ERROR: AddressSanitizer: heap-use-after-free on address 0x7b60767e33d0 at pc 0x55c1c3d0de8e bp 0x7ffe51b1f950 sp 0x7ffe51b1f940
READ of size 4 at 0x7b60767e33d0 thread T0
#0 0x55c1c3d0de8d in timeout_callback src/shared/timeout-mainloop.c:33
#1 0x55c1c3d0eb75 in timeout_callback src/shared/mainloop.c:270
#2 0x55c1c3d0faa6 in mainloop_run src/shared/mainloop.c:124
#3 0x55c1c3d123fa in mainloop_run_with_signal src/shared/mainloop-notify.c:196
#4 0x55c1c3d1764b in tester_run src/shared/tester.c:1223
#5 0x55c1c3c4117d in main tools/l2cap-tester.c:3918
#6 0x7f207777f600 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:59
#7 0x7f207777f717 in __libc_start_main_impl ../csu/libc-start.c:360
#8 0x55c1c3c47c84 in _start (/home/runner/work/bluetooth-next/bluetooth-next/src/bluez/tools/l2cap-tester+0x16cc84) (BuildId: 91c9d49e9766c394468e27ffa29b05e7d348169f)
0x7b60767e33d0 is located 0 bytes inside of 40-byte region [0x7b60767e33d0,0x7b60767e33f8)
freed by thread T0 here:
#0 0x7f20780bd3ff in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:51
#1 0x55c1c3d0e36a in timeout_destroy src/shared/mainloop.c:251
previously allocated by thread T0 here:
#0 0x7f20780be60f in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:67
#1 0x55c1c3cf82d5 in util_malloc src/shared/util.c:46
SUMMARY: AddressSanitizer: heap-use-after-free src/shared/timeout-mainloop.c:33 in timeout_callback
No test result found
https://github.com/bluez/bluetooth-next/pull/1055
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v1 2/6] Bluetooth: ISO: Add new QoS v2 adding rate field
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 0/6] Bluetooth: Add support for HDT and EKS Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 1/6] Bluetooth: Add initial support for HDT PHY Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 3/6] Bluetooth: Add initial support for EKS Luiz Augusto von Dentz
` (4 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add struct bt_iso_qos_v2, which extends the I/O QoS of BT_ISO_QOS with
a rates field so userspace can select the HDT rates of a CIS or BIS
along with BT_ISO_PHY_HDT. BT_ISO_QOS accepts either version, telling
them apart by the length of the option, and the ISO sockets keep the v2
form internally so the rates are passed to LE Set CIG Parameters v2 and
LE Create BIG v2 and reported back from the CIS Established and Create
BIG Complete events.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/bluetooth.h | 44 ++++++++++
include/net/bluetooth/hci_core.h | 15 ++--
include/net/bluetooth/hci_sync.h | 2 +-
net/bluetooth/hci_conn.c | 26 +++---
net/bluetooth/hci_event.c | 8 +-
net/bluetooth/hci_sync.c | 44 ++++++----
net/bluetooth/iso.c | 138 ++++++++++++++++++++++++++----
7 files changed, 218 insertions(+), 59 deletions(-)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index aab6a1ac9a3e..945c0bc58a95 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -235,6 +235,50 @@ struct bt_iso_qos {
};
};
+struct bt_iso_io_qos_v2 {
+ __u32 interval;
+ __u16 latency;
+ __u16 sdu;
+ __u8 phys;
+ __u8 rtn;
+ __u16 rates;
+};
+
+struct bt_iso_ucast_qos_v2 {
+ __u8 cig;
+ __u8 cis;
+ __u8 sca;
+ __u8 packing;
+ __u8 framing;
+ struct bt_iso_io_qos_v2 in;
+ struct bt_iso_io_qos_v2 out;
+};
+
+struct bt_iso_bcast_qos_v2 {
+ __u8 big;
+ __u8 bis;
+ __u8 sync_factor;
+ __u8 packing;
+ __u8 framing;
+ struct bt_iso_io_qos_v2 in;
+ struct bt_iso_io_qos_v2 out;
+ __u8 encryption;
+ __u8 bcode[16];
+ __u8 options;
+ __u16 skip;
+ __u16 sync_timeout;
+ __u8 sync_cte_type;
+ __u8 mse;
+ __u16 timeout;
+};
+
+struct bt_iso_qos_v2 {
+ union {
+ struct bt_iso_ucast_qos_v2 ucast;
+ struct bt_iso_bcast_qos_v2 bcast;
+ };
+};
+
#define BT_ISO_PHY_1M BIT(0)
#define BT_ISO_PHY_2M BIT(1)
#define BT_ISO_PHY_CODED BIT(2)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6a16bc1e775c..f6e2a414bf2a 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -797,7 +797,7 @@ struct hci_conn {
__s8 rssi;
__s8 tx_power;
__s8 max_tx_power;
- struct bt_iso_qos iso_qos;
+ struct bt_iso_qos_v2 iso_qos;
__u8 num_bis;
__u8 bis[HCI_MAX_ISO_BIS];
@@ -1678,23 +1678,24 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u16 setting, struct bt_codec *codec,
u16 timeout);
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 dst_type, struct bt_iso_qos *qos,
+ __u8 dst_type, struct bt_iso_qos_v2 *qos,
u16 timeout);
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
- struct bt_iso_qos *qos,
+ struct bt_iso_qos_v2 *qos,
__u8 base_len, __u8 *base, u16 timeout);
int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type);
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 dst_type, struct bt_iso_qos *qos,
+ __u8 dst_type, struct bt_iso_qos_v2 *qos,
u16 timeout);
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
- struct bt_iso_qos *qos,
+ struct bt_iso_qos_v2 *qos,
__u8 data_len, __u8 *data, u16 timeout);
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 dst_type, __u8 sid, struct bt_iso_qos *qos);
+ __u8 dst_type, __u8 sid,
+ struct bt_iso_qos_v2 *qos);
int hci_conn_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
- struct bt_iso_qos *qos, __u16 sync_handle,
+ struct bt_iso_qos_v2 *qos, __u16 sync_handle,
__u8 num_bis, __u8 bis[]);
int hci_conn_check_link_mode(struct hci_conn *conn);
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index a24f6a84a447..ca21fe81a06e 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -195,5 +195,5 @@ int hci_le_read_remote_features(struct hci_conn *conn);
int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type);
int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys, u16 phy_opts);
int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id);
-int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos *qos,
+int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos_v2 *qos,
u8 num_bis);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index edf7be18a0cc..8524fca9f273 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1637,7 +1637,7 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
return 0;
}
-static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
+static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos_v2 *qos)
{
struct hci_conn *conn;
u8 big;
@@ -1661,7 +1661,7 @@ static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
return 0;
}
-static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
+static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos_v2 *qos)
{
struct hci_conn *conn;
u8 bis;
@@ -1703,7 +1703,7 @@ static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
/* This function requires the caller holds hdev->lock */
static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 sid, struct bt_iso_qos *qos,
+ __u8 sid, struct bt_iso_qos_v2 *qos,
__u8 base_len, __u8 *base, u16 timeout)
{
struct hci_conn *conn;
@@ -1938,7 +1938,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
return sco;
}
-static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
+static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos_v2 *qos)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data data;
@@ -1961,7 +1961,7 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
return hci_le_set_cig_params_sync(hdev, cig_id);
}
-static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
+static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos_v2 *qos)
{
struct hci_dev *hdev = conn->hdev;
struct iso_list_data data;
@@ -2023,7 +2023,7 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
}
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 dst_type, struct bt_iso_qos *qos,
+ __u8 dst_type, struct bt_iso_qos_v2 *qos,
u16 timeout)
{
struct hci_conn *cis;
@@ -2166,7 +2166,7 @@ int hci_le_create_cis_pending(struct hci_dev *hdev)
}
static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
- struct bt_iso_io_qos *qos, __u8 phys)
+ struct bt_iso_io_qos_v2 *qos, __u8 phys)
{
/* Only set MTU if PHY is enabled */
if (!qos->sdu && qos->phys)
@@ -2189,7 +2189,7 @@ static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
static int create_big_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
- struct bt_iso_qos *qos = &conn->iso_qos;
+ struct bt_iso_qos_v2 *qos = &conn->iso_qos;
u16 interval, sync_interval = 0;
u32 flags = 0;
int err;
@@ -2218,7 +2218,7 @@ static int create_big_sync(struct hci_dev *hdev, void *data)
struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
- struct bt_iso_qos *qos)
+ struct bt_iso_qos_v2 *qos)
{
struct hci_conn *conn;
@@ -2241,7 +2241,7 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
}
int hci_conn_big_create_sync(struct hci_dev *hdev, struct hci_conn *hcon,
- struct bt_iso_qos *qos, __u16 sync_handle,
+ struct bt_iso_qos_v2 *qos, __u16 sync_handle,
__u8 num_bis, __u8 bis[])
{
int err;
@@ -2292,7 +2292,7 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
}
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
- struct bt_iso_qos *qos,
+ struct bt_iso_qos_v2 *qos,
__u8 base_len, __u8 *base, u16 timeout)
{
struct hci_conn *conn;
@@ -2380,7 +2380,7 @@ static void bis_mark_per_adv(struct hci_conn *conn, void *data)
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 dst_type, __u8 sid,
- struct bt_iso_qos *qos,
+ struct bt_iso_qos_v2 *qos,
__u8 base_len, __u8 *base, u16 timeout)
{
struct hci_conn *conn;
@@ -2431,7 +2431,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
}
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
- __u8 dst_type, struct bt_iso_qos *qos,
+ __u8 dst_type, struct bt_iso_qos_v2 *qos,
u16 timeout)
{
struct hci_conn *le;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 466f09124fcd..7b1c2ce58d73 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -7372,7 +7372,7 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data,
{
struct hci_evt_le_cis_established *ev = data;
struct hci_conn *conn;
- struct bt_iso_qos *qos;
+ struct bt_iso_qos_v2 *qos;
bool pending = false;
u16 handle = __le16_to_cpu(ev->handle);
u32 c_sdu_interval, p_sdu_interval;
@@ -7869,7 +7869,7 @@ static void hci_le_cis_established_evt_v3(struct hci_dev *hdev, void *data,
{
struct hci_evt_le_cis_established_v3 *ev = data;
struct hci_conn *conn;
- struct bt_iso_qos *qos;
+ struct bt_iso_qos_v2 *qos;
bool pending = false;
u16 handle = __le16_to_cpu(ev->handle);
u32 c_sdu_interval, p_sdu_interval;
@@ -7915,6 +7915,8 @@ static void hci_le_cis_established_evt_v3(struct hci_dev *hdev, void *data,
qos->ucast.out.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0;
qos->ucast.in.phys = le_phy_qos(ev->c_phy);
qos->ucast.out.phys = le_phy_qos(ev->p_phy);
+ qos->ucast.in.rates = le16_to_cpu(ev->c_rates);
+ qos->ucast.out.rates = le16_to_cpu(ev->p_rates);
break;
case HCI_ROLE_MASTER:
qos->ucast.in.interval = p_sdu_interval;
@@ -7930,6 +7932,8 @@ static void hci_le_cis_established_evt_v3(struct hci_dev *hdev, void *data,
qos->ucast.in.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0;
qos->ucast.out.phys = le_phy_qos(ev->c_phy);
qos->ucast.in.phys = le_phy_qos(ev->p_phy);
+ qos->ucast.out.rates = le16_to_cpu(ev->c_rates);
+ qos->ucast.in.rates = le16_to_cpu(ev->p_rates);
break;
}
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index a076ff131bc4..f5ee261af67a 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -3187,7 +3187,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
conn = hci_conn_hash_lookup_ba(hdev, PA_LINK,
&bdaddr);
if (conn) {
- struct bt_iso_qos *qos = &conn->iso_qos;
+ struct bt_iso_qos_v2 *qos = &conn->iso_qos;
if (qos->bcast.in.phys & BT_ISO_PHY_1M ||
qos->bcast.in.phys & BT_ISO_PHY_2M) {
@@ -7871,7 +7871,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
- u16 acl_handle, struct bt_iso_qos *qos)
+ u16 acl_handle, struct bt_iso_qos_v2 *qos)
{
struct hci_cp_le_past_params cp;
int err;
@@ -7905,7 +7905,7 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
{
struct hci_cp_le_pa_create_sync cp;
struct hci_conn *conn = data, *le;
- struct bt_iso_qos *qos = &conn->iso_qos;
+ struct bt_iso_qos_v2 *qos = &conn->iso_qos;
int err;
if (!hci_conn_valid(hdev, conn))
@@ -8046,7 +8046,7 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis,
HCI_MAX_ISO_BIS);
struct hci_conn *conn = data;
- struct bt_iso_qos *qos = &conn->iso_qos;
+ struct bt_iso_qos_v2 *qos = &conn->iso_qos;
int err;
if (!hci_conn_valid(hdev, conn))
@@ -8374,7 +8374,7 @@ static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
DEFINE_FLEX(struct hci_cp_le_set_cig_params_v2, pdu, cis, num_cis,
0x1f);
struct hci_conn *conn;
- struct bt_iso_qos *qos;
+ struct bt_iso_qos_v2 *qos;
u8 aux_num_cis = 0;
u8 cis_id;
@@ -8420,8 +8420,15 @@ static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
qos->ucast.out.phys;
cis->c_rtn = qos->ucast.out.rtn;
cis->p_rtn = qos->ucast.in.rtn;
+ cis->c_hdt_rates = qos->ucast.out.rates ? qos->ucast.out.rates :
+ qos->ucast.in.rates;
+ cis->p_hdt_rates = qos->ucast.in.rates ? qos->ucast.in.rates :
+ qos->ucast.out.rates;
- if (cis->c_phys & BIT(4)) {
+ /* If HDT PHY is set but rates are not set then enable all HDT
+ * supported rates.
+ */
+ if (cis->c_phys & BIT(4) && !cis->c_hdt_rates) {
cis->c_hdt_rates |= BIT(0); /* HDT 2M */
cis->c_hdt_rates |= BIT(1); /* HDT 3M */
cis->c_hdt_rates |= BIT(2); /* HDT 4M */
@@ -8433,7 +8440,7 @@ static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
cis->c_hdt_rates |= BIT(4); /* HDT 7.5M */
}
- if (cis->p_phys & BIT(4)) {
+ if (cis->p_phys & BIT(4) && !cis->p_hdt_rates) {
cis->p_hdt_rates |= BIT(0); /* HDT 2M */
cis->p_hdt_rates |= BIT(1); /* HDT 3M */
cis->p_hdt_rates |= BIT(2); /* HDT 4M */
@@ -8462,7 +8469,7 @@ int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id)
{
DEFINE_FLEX(struct hci_cp_le_set_cig_params, pdu, cis, num_cis, 0x1f);
struct hci_conn *conn;
- struct bt_iso_qos *qos;
+ struct bt_iso_qos_v2 *qos;
u8 aux_num_cis = 0;
u8 cis_id;
@@ -8525,7 +8532,7 @@ int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id)
}
static int hci_le_create_big_v2_sync(struct hci_conn *conn,
- struct bt_iso_qos *qos, u8 num_bis)
+ struct bt_iso_qos_v2 *qos, u8 num_bis)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_le_create_big_v2 cp;
@@ -8543,25 +8550,28 @@ static int hci_le_create_big_v2_sync(struct hci_conn *conn,
cp.bis.framing = qos->bcast.framing;
cp.bis.encryption = qos->bcast.encryption;
memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
+ cp.bis.hdt_rates = qos->bcast.out.rates ? qos->bcast.out.rates : 0x00;
- /* Check if HDT PHY is set then enable HDT supported rates */
- if (cp.bis.phys & BIT(4)) {
- cp.bis.coded_rates |= BIT(0); /* HDT 2M */
- cp.bis.coded_rates |= BIT(1); /* HDT 3M */
- cp.bis.coded_rates |= BIT(2); /* HDT 4M */
+ /* If HDT PHY is set but rates are not set then enable all HDT
+ * supported rates.
+ */
+ if (cp.bis.phys & BIT(4) && !cp.bis.hdt_rates) {
+ cp.bis.hdt_rates |= BIT(0); /* HDT 2M */
+ cp.bis.hdt_rates |= BIT(1); /* HDT 3M */
+ cp.bis.hdt_rates |= BIT(2); /* HDT 4M */
if (hdt_6_tx_capable(hdev))
- cp.bis.coded_rates |= BIT(3); /* HDT 6M */
+ cp.bis.hdt_rates |= BIT(3); /* HDT 6M */
if (hdt_7_5_tx_capable(hdev))
- cp.bis.coded_rates |= BIT(4); /* HDT 7.5M */
+ cp.bis.hdt_rates |= BIT(4); /* HDT 7.5M */
}
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_BIG_V2,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
-int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos *qos,
+int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos_v2 *qos,
u8 num_bis)
{
struct hci_dev *hdev = conn->hdev;
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 06f1e7ba2cb1..15b3448ccd70 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -75,7 +75,7 @@ struct iso_pinfo {
__u8 bc_bis[ISO_MAX_NUM_BIS];
__u16 sync_handle;
unsigned long flags;
- struct bt_iso_qos qos;
+ struct bt_iso_qos_v2 qos;
bool qos_user_set;
__u8 base_len;
__u8 base[BASE_MAX_LENGTH];
@@ -83,10 +83,10 @@ struct iso_pinfo {
struct delayed_work timeout_work;
};
-static struct bt_iso_qos default_qos;
+static struct bt_iso_qos_v2 default_qos;
-static bool check_ucast_qos(struct bt_iso_qos *qos);
-static bool check_bcast_qos(struct bt_iso_qos *qos);
+static bool check_ucast_qos(struct bt_iso_qos_v2 *qos);
+static bool check_bcast_qos(struct bt_iso_qos_v2 *qos);
static bool iso_match_sid(struct sock *sk, void *data);
static bool iso_match_sid_past(struct sock *sk, void *data);
static bool iso_match_sync_handle(struct sock *sk, void *data);
@@ -620,7 +620,7 @@ static int iso_connect_cis(struct sock *sk)
return err;
}
-static struct bt_iso_qos *iso_sock_get_qos(struct sock *sk)
+static struct bt_iso_qos_v2 *iso_sock_get_qos(struct sock *sk)
{
if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONNECT2)
return &iso_pi(sk)->conn->hcon->iso_qos;
@@ -628,11 +628,49 @@ static struct bt_iso_qos *iso_sock_get_qos(struct sock *sk)
return &iso_pi(sk)->qos;
}
+static void iso_qos_io_v1(struct bt_iso_io_qos_v2 *qos,
+ struct bt_iso_io_qos *old)
+{
+ memcpy(old, qos, sizeof(*old));
+}
+
+static void iso_bcast_qos_v1(struct bt_iso_qos_v2 *qos, struct bt_iso_qos *old)
+{
+ memcpy(&old->bcast, &qos->bcast,
+ offsetof(struct bt_iso_bcast_qos, in));
+ iso_qos_io_v1(&qos->bcast.in, &old->bcast.in);
+ iso_qos_io_v1(&qos->bcast.out, &old->bcast.out);
+ memcpy(&old->bcast + offsetof(struct bt_iso_bcast_qos, out),
+ &qos->bcast + offsetof(struct bt_iso_bcast_qos_v2, out),
+ offsetofend(struct bt_iso_bcast_qos, timeout));
+}
+
+static void iso_ucast_qos_v1(struct bt_iso_qos_v2 *qos, struct bt_iso_qos *old)
+{
+ memcpy(&old->ucast, &qos->ucast,
+ offsetof(struct bt_iso_ucast_qos, in));
+ iso_qos_io_v1(&qos->ucast.in, &old->ucast.in);
+ iso_qos_io_v1(&qos->ucast.out, &old->ucast.out);
+ memcpy(&old->ucast + offsetof(struct bt_iso_bcast_qos, out),
+ &qos->ucast + offsetof(struct bt_iso_bcast_qos_v2, out),
+ offsetofend(struct bt_iso_bcast_qos, timeout));
+}
+
+static void iso_sock_get_qos_v1(struct sock *sk, struct bt_iso_qos *old)
+{
+ struct bt_iso_qos_v2 *qos = iso_sock_get_qos(sk);
+
+ if (!bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
+ iso_bcast_qos_v1(qos, old);
+ else
+ iso_ucast_qos_v1(qos, old);
+}
+
static int iso_send_frame(struct sock *sk, struct sk_buff *skb,
const struct sockcm_cookie *sockc)
{
struct iso_conn *conn = iso_pi(sk)->conn;
- struct bt_iso_qos *qos = iso_sock_get_qos(sk);
+ struct bt_iso_qos_v2 *qos = iso_sock_get_qos(sk);
struct hci_iso_data_hdr *hdr;
int len = 0;
@@ -1009,7 +1047,7 @@ static struct proto iso_proto = {
.rtn = 2u, \
}
-static struct bt_iso_qos default_qos = {
+static struct bt_iso_qos_v2 default_qos = {
.bcast = {
.big = BT_ISO_QOS_BIG_UNSET,
.bis = BT_ISO_QOS_BIS_UNSET,
@@ -1863,7 +1901,7 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
return bt_sock_recvmsg(sock, msg, len, flags);
}
-static bool check_io_qos(struct bt_iso_io_qos *qos)
+static bool check_io_qos(struct bt_iso_io_qos_v2 *qos)
{
/* If no PHY is enable SDU must be 0 */
if (!qos->phys && qos->sdu)
@@ -1881,7 +1919,7 @@ static bool check_io_qos(struct bt_iso_io_qos *qos)
return true;
}
-static bool check_ucast_qos(struct bt_iso_qos *qos)
+static bool check_ucast_qos(struct bt_iso_qos_v2 *qos)
{
if (qos->ucast.cig > 0xef && qos->ucast.cig != BT_ISO_QOS_CIG_UNSET)
return false;
@@ -1907,7 +1945,7 @@ static bool check_ucast_qos(struct bt_iso_qos *qos)
return true;
}
-static bool check_bcast_qos(struct bt_iso_qos *qos)
+static bool check_bcast_qos(struct bt_iso_qos_v2 *qos)
{
if (!qos->bcast.sync_factor)
qos->bcast.sync_factor = 0x01;
@@ -1954,12 +1992,51 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
return true;
}
+static void iso_qos_io_v2(struct bt_iso_io_qos_v2 *qos,
+ struct bt_iso_io_qos *old)
+{
+ memcpy(qos, old, sizeof(*old));
+ qos->rates = 0x00;
+}
+
+static void iso_bcast_qos_v2(struct bt_iso_qos_v2 *qos, struct bt_iso_qos *old)
+{
+ memcpy(&qos->bcast, &old->bcast,
+ offsetof(struct bt_iso_bcast_qos_v2, in));
+ iso_qos_io_v2(&qos->bcast.in, &old->bcast.in);
+ iso_qos_io_v2(&qos->bcast.out, &old->bcast.out);
+ memcpy(&qos->bcast + offsetof(struct bt_iso_bcast_qos_v2, out),
+ &old->bcast + offsetof(struct bt_iso_bcast_qos, out),
+ offsetofend(struct bt_iso_bcast_qos_v2, timeout));
+}
+
+static void iso_ucast_qos_v2(struct bt_iso_qos_v2 *qos, struct bt_iso_qos *old)
+{
+ memcpy(&qos->ucast, &old->ucast,
+ offsetof(struct bt_iso_ucast_qos_v2, in));
+ iso_qos_io_v2(&qos->ucast.in, &old->ucast.in);
+ iso_qos_io_v2(&qos->ucast.out, &old->ucast.out);
+ memcpy(&qos->ucast + offsetof(struct bt_iso_bcast_qos_v2, out),
+ &old->ucast + offsetof(struct bt_iso_bcast_qos, out),
+ offsetofend(struct bt_iso_bcast_qos_v2, timeout));
+}
+
+static void iso_qos_v2(struct sock *sk, struct bt_iso_qos_v2 *qos,
+ struct bt_iso_qos *old)
+{
+ if (!bacmp(&iso_pi(sk)->dst, BDADDR_ANY))
+ iso_bcast_qos_v2(qos, old);
+ else
+ iso_ucast_qos_v2(qos, old);
+}
+
static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
- struct bt_iso_qos qos = default_qos;
+ struct bt_iso_qos_v2 qos = default_qos;
+ struct bt_iso_qos qos_old;
u32 opt;
BT_DBG("sk %p", sk);
@@ -2005,9 +2082,21 @@ static int iso_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
- err = copy_safe_from_sockptr(&qos, sizeof(qos), optval, optlen);
- if (err)
+ if (optlen == sizeof(qos)) {
+ err = copy_safe_from_sockptr(&qos, sizeof(qos), optval,
+ optlen);
+ if (err)
+ break;
+ } else if (optlen == sizeof(qos_old)) {
+ err = copy_safe_from_sockptr(&qos_old, sizeof(qos_old),
+ optval, optlen);
+ if (err)
+ break;
+ iso_qos_v2(sk, &qos, &qos_old);
+ } else {
+ err = -EINVAL;
break;
+ }
iso_pi(sk)->qos = qos;
iso_pi(sk)->qos_user_set = true;
@@ -2048,7 +2137,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
sockopt_t *opt)
{
struct sock *sk = sock->sk;
- struct bt_iso_qos *qos;
+ struct bt_iso_qos_v2 *qos;
+ struct bt_iso_qos qos_old;
int len, val, err = 0;
u8 base_len;
u8 *base;
@@ -2081,11 +2171,21 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
break;
case BT_ISO_QOS:
- qos = iso_sock_get_qos(sk);
-
- len = min_t(unsigned int, len, sizeof(*qos));
- if (copy_to_iter(qos, len, &opt->iter_out) != len)
- err = -EFAULT;
+ if (len == sizeof(*qos)) {
+ qos = iso_sock_get_qos(sk);
+
+ len = min_t(unsigned int, len, sizeof(*qos));
+ if (copy_to_iter(qos, len, &opt->iter_out) != len)
+ err = -EFAULT;
+ } else if (len == sizeof(qos_old)) {
+ iso_sock_get_qos_v1(sk, &qos_old);
+
+ len = min_t(unsigned int, len, sizeof(qos_old));
+ if (copy_to_iter(&qos_old, len, &opt->iter_out) != len)
+ err = -EFAULT;
+ } else {
+ err = -EINVAL;
+ }
break;
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v1 3/6] Bluetooth: Add initial support for EKS
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
` (2 preceding siblings ...)
2026-11-03 22:19 ` [PATCH v1 2/6] Bluetooth: ISO: Add new QoS v2 adding rate field Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 4/6] Bluetooth: L2CAP: Add MIC length validation for HDT PHY Luiz Augusto von Dentz
` (3 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add support for Encryption Key Schedule (EKS) introduced in Bluetooth
7.0:
- LE Enable Encryption v2, which replaces LE Start Encryption on EKS
capable controllers and carries the MIC length to use on the LE HDT
PHY. SMP now starts encryption through hci_le_enable_encrypt(),
which picks the version the controller supports.
- Encryption Change v3 and Encryption Key Refresh Complete v2, enabled
through Event Mask Page 2, which report the MIC length and whether
the key schedule is in use. The encryption change and key refresh
handling is moved to helpers shared by every version of the events.
- eks_capable() for the Encryption Key Schedule feature bit.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/hci.h | 30 +++++++
include/net/bluetooth/hci_core.h | 4 +-
include/net/bluetooth/hci_sync.h | 3 +
net/bluetooth/hci_conn.c | 18 ----
net/bluetooth/hci_event.c | 149 +++++++++++++++++++++----------
net/bluetooth/hci_sync.c | 85 ++++++++++++++++++
net/bluetooth/smp.c | 7 +-
7 files changed, 228 insertions(+), 68 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 525344702c87..e9bbd687b12e 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -2564,6 +2564,16 @@ struct hci_rp_le_read_def_data_len_v2 {
__le16 tx_time;
} __packed;
+#define HCI_OP_LE_ENABLE_ENCRYPT_V2 0x20af
+struct hci_cp_le_enable_encrypt_v2 {
+ __le16 handle;
+ __le64 rand;
+ __le16 ediv;
+ __u8 ltk[16];
+ __u8 mic_len;
+ __u8 encryption;
+} __packed;
+
#define HCI_OP_LE_READ_MAX_DATA_LEN_V2 0x20b0
struct hci_cp_le_read_max_data_len_v2 {
__u8 phy;
@@ -3040,6 +3050,26 @@ struct hci_ev_sync_train_complete {
#define HCI_EV_PERIPHERAL_PAGE_RESP_TIMEOUT 0x54
+#define HCI_EVT_KEY_REFRESH_COMPLETE_V2 0x5b
+struct hci_evt_key_refresh_complete_v2 {
+ __u8 status;
+ __le16 handle;
+ __u8 mic_len;
+ __u8 eks_mode;
+ __u8 eks_debug;
+} __packed;
+
+#define HCI_EVT_ENCRYPT_CHANGE_V3 0x5a
+struct hci_evt_encrypt_change_v3 {
+ __u8 status;
+ __le16 handle;
+ __u8 mode;
+ __u8 key_size;
+ __u8 mic_len;
+ __u8 eks_mode;
+ __u8 eks_debug;
+} __packed;
+
#define HCI_EV_LE_CONN_COMPLETE 0x01
struct hci_ev_le_conn_complete {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index f6e2a414bf2a..60318740ec99 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -2181,6 +2181,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
((conn)->le_tx_def_phys & HCI_LE_SET_PHY_HDT)
#define hdt_rx_capable(conn) \
((conn)->le_rx_def_phys & HCI_LE_SET_PHY_HDT)
+#define eks_capable(dev) \
+ (le_feature(dev, 1, 1) & HCI_LE_EKS_FEATURE)
/* Channel sounding support */
#define le_cs_capable(dev) \
@@ -2628,8 +2630,6 @@ void mgmt_conn_subrate_notify(struct hci_dev *hdev, struct hci_conn *conn,
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
-void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
- __u8 ltk[16], __u8 key_size);
void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 *bdaddr_type);
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index ca21fe81a06e..4b5baee9ce11 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -197,3 +197,6 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys, u16 phy_opts);
int hci_le_set_cig_params_sync(struct hci_dev *hdev, u8 cig_id);
int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos_v2 *qos,
u8 num_bis);
+
+int hci_le_enable_encrypt(struct hci_conn *conn, __le16 ediv, __le64 rand,
+ __u8 ltk[16], __u8 key_size);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8524fca9f273..90aed3b75634 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -598,24 +598,6 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
}
}
-void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
- __u8 ltk[16], __u8 key_size)
-{
- struct hci_dev *hdev = conn->hdev;
- struct hci_cp_le_start_enc cp;
-
- BT_DBG("hcon %p", conn);
-
- memset(&cp, 0, sizeof(cp));
-
- cp.handle = cpu_to_le16(conn->handle);
- cp.rand = rand;
- cp.ediv = ediv;
- memcpy(cp.ltk, ltk, key_size);
-
- hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
-}
-
/* Device _must_ be locked */
void hci_sco_setup(struct hci_conn *conn, __u8 status)
{
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 7b1c2ce58d73..896a2ebd806d 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3725,22 +3725,12 @@ static void hci_remote_name_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
-static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
- struct sk_buff *skb)
+static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt)
{
- struct hci_ev_encrypt_change *ev = data;
- struct hci_conn *conn;
-
- bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
- if (!conn)
- goto unlock;
+ struct hci_dev *hdev = conn->hdev;
- if (!ev->status) {
- if (ev->encrypt) {
+ if (!status) {
+ if (encrypt) {
/* Encryption implies authentication */
set_bit(HCI_CONN_AUTH, conn->flags);
set_bit(HCI_CONN_ENCRYPT, conn->flags);
@@ -3750,7 +3740,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
set_bit(HCI_CONN_FIPS, conn->flags);
- if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
+ if ((conn->type == ACL_LINK && encrypt == 0x02) ||
conn->type == LE_LINK)
set_bit(HCI_CONN_AES_CCM, conn->flags);
@@ -3781,7 +3771,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
/* We should disregard the current RPA and generate a new one
* whenever the encryption procedure fails.
*/
- if (ev->status && conn->type == LE_LINK) {
+ if (status && conn->type == LE_LINK) {
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
hci_adv_instances_set_rpa_expired(hdev, true);
}
@@ -3790,23 +3780,23 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
/* Check link security requirements are met */
if (!hci_conn_check_link_mode(conn))
- ev->status = HCI_ERROR_AUTH_FAILURE;
+ status = HCI_ERROR_AUTH_FAILURE;
- if (ev->status && conn->state == BT_CONNECTED) {
- if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING)
+ if (status && conn->state == BT_CONNECTED) {
+ if (status == HCI_ERROR_PIN_OR_KEY_MISSING)
set_bit(HCI_CONN_AUTH_FAILURE, conn->flags);
/* Notify upper layers so they can cleanup before
* disconnecting.
*/
- hci_encrypt_cfm(conn, ev->status);
+ hci_encrypt_cfm(conn, status);
hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
hci_conn_drop(conn);
- goto unlock;
+ return;
}
/* Try reading the encryption key size for encrypted ACL links */
- if (!ev->status && ev->encrypt && conn->type == ACL_LINK) {
+ if (!status && encrypt && conn->type == ACL_LINK) {
if (hci_read_enc_key_size(hdev, conn)) {
/* Could not even issue the key-size read: report the
* security level now since hci_cc_read_enc_key_size()
@@ -3816,7 +3806,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
goto notify;
}
- goto unlock;
+ return;
}
/* We skip the WRITE_AUTH_PAYLOAD_TIMEOUT for ATS2851 based controllers
@@ -3846,7 +3836,44 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
}
notify:
- hci_encrypt_cfm(conn, ev->status);
+ hci_encrypt_cfm(conn, status);
+}
+
+static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_ev_encrypt_change *ev = data;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (!conn)
+ goto unlock;
+
+ hci_change_encrypt(conn, ev->status, ev->encrypt);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void hci_encrypt_change_v3_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_evt_encrypt_change_v3 *ev = data;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (!conn)
+ goto unlock;
+
+ hci_change_encrypt(conn, ev->status, ev->mode);
unlock:
hci_dev_unlock(hdev);
@@ -5546,28 +5573,15 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, void *edata,
hci_dev_unlock(hdev);
}
-static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data,
- struct sk_buff *skb)
+static void hci_conn_refresh_key(struct hci_conn *conn, u8 status)
{
- struct hci_ev_key_refresh_complete *ev = data;
- struct hci_conn *conn;
-
- bt_dev_dbg(hdev, "status 0x%2.2x handle 0x%4.4x", ev->status,
- __le16_to_cpu(ev->handle));
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
- if (!conn)
- goto unlock;
-
/* For BR/EDR the necessary steps are taken through the
* auth_complete event.
*/
if (conn->type != LE_LINK)
- goto unlock;
+ return;
- if (!ev->status) {
+ if (!status) {
bool sec_level_changed =
conn->sec_level != conn->pending_sec_level;
@@ -5578,25 +5592,64 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data,
clear_bit(HCI_CONN_ENCRYPT_PEND, conn->flags);
- if (ev->status && conn->state == BT_CONNECTED) {
+ if (status && conn->state == BT_CONNECTED) {
hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
hci_conn_drop(conn);
- goto unlock;
+ return;
}
if (conn->state == BT_CONFIG) {
- if (!ev->status)
+ if (!status)
conn->state = BT_CONNECTED;
- hci_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, status);
hci_conn_drop(conn);
} else {
- hci_auth_cfm(conn, ev->status);
+ hci_auth_cfm(conn, status);
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_drop(conn);
}
+}
+
+static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_ev_key_refresh_complete *ev = data;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x handle 0x%4.4x", ev->status,
+ __le16_to_cpu(ev->handle));
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (!conn)
+ goto unlock;
+
+ hci_conn_refresh_key(conn, ev->status);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void hci_key_refresh_complete_v2_evt(struct hci_dev *hdev, void *data,
+ struct sk_buff *skb)
+{
+ struct hci_evt_key_refresh_complete_v2 *ev = data;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x handle 0x%4.4x", ev->status,
+ __le16_to_cpu(ev->handle));
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (!conn)
+ goto unlock;
+
+ hci_conn_refresh_key(conn, ev->status);
unlock:
hci_dev_unlock(hdev);
@@ -8409,6 +8462,12 @@ static const struct hci_ev {
/* [0x3e = HCI_EV_LE_META] */
HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt,
sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_PLEN),
+ /* [0xfb = HCI_EVT_LE_ENCRYPT_KEY_REFRESH_COMPLETE_V2] */
+ HCI_EV(HCI_EVT_KEY_REFRESH_COMPLETE_V2, hci_key_refresh_complete_v2_evt,
+ sizeof(struct hci_evt_key_refresh_complete_v2)),
+ /* [0xfc = HCI_EVT_LE_ENCRYPT_CHANGE_V3 */
+ HCI_EV(HCI_EVT_ENCRYPT_CHANGE_V3, hci_encrypt_change_v3_evt,
+ sizeof(struct hci_evt_encrypt_change_v3)),
/* [0xff = HCI_EV_VENDOR] */
HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_PLEN),
};
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index f5ee261af67a..1c52b914c236 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5177,6 +5177,13 @@ static int hci_set_event_mask_page_2_sync(struct hci_dev *hdev)
changed = true;
}
+ if (eks_capable(hdev)) {
+ /* EKS events */
+ events[3] |= BIT(2); /* Encrytion Change v3 */
+ events[3] |= BIT(3); /* Encrytion Key Refresh Complete v2 */
+ changed = true;
+ }
+
/* Some Broadcom based controllers indicate support for Set Event
* Mask Page 2 command, but then actually do not support it. Since
* the default value is all bits set to zero, the command is only
@@ -8424,6 +8431,7 @@ static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
qos->ucast.in.rates;
cis->p_hdt_rates = qos->ucast.in.rates ? qos->ucast.in.rates :
qos->ucast.out.rates;
+ cis->mic_len = 0x02; /* Always prefer 128 bits? */
/* If HDT PHY is set but rates are not set then enable all HDT
* supported rates.
@@ -8597,3 +8605,80 @@ int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos_v2 *qos,
return __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_BIG,
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
+
+static void le_enable_encrypt_complete(struct hci_dev *hdev, void *data, int err)
+{
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(data);
+}
+
+static int hci_le_enable_encrypt_v2_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_enable_encrypt_v2 *cp = data;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_ENABLE_ENCRYPT_V2,
+ sizeof(*cp), cp, HCI_CMD_TIMEOUT);
+}
+
+static int hci_le_enable_encrypt_v2(struct hci_conn *conn, __le16 ediv,
+ __le64 rand, __u8 ltk[16])
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_enable_encrypt_v2 *cp;
+
+ bt_dev_dbg(hdev, "hcon %p", conn);
+
+ cp = kzalloc_obj(*cp);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->rand = rand;
+ cp->ediv = ediv;
+ cp->mic_len = 0x02; /* Always prefer 128 bits? */
+
+ if (ltk) {
+ memcpy(cp->ltk, ltk, sizeof(cp->ltk));
+ cp->encryption = 0x00; /* Use LTK provided in LTK field */
+ } else {
+ cp->encryption = 0x01; /* Use opportunistic with EKS */
+ }
+
+ return hci_cmd_sync_queue_once(hdev, hci_le_enable_encrypt_v2_sync, cp,
+ le_enable_encrypt_complete);
+}
+
+static int hci_le_enable_encrypt_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_start_enc *cp = data;
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_START_ENC,
+ sizeof(*cp), cp,
+ HCI_CMD_TIMEOUT);
+}
+
+int hci_le_enable_encrypt(struct hci_conn *conn, __le16 ediv, __le64 rand,
+ __u8 ltk[16], __u8 key_size)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_start_enc *cp;
+
+ /* Check if EKS and HCI_OP_LE_ENABLE_ENCRYPT_V2 are supported */
+ if (eks_capable(hdev) && (hdev->commands[50] & BIT(4)))
+ return hci_le_enable_encrypt_v2(conn, ediv, rand, ltk);
+
+ bt_dev_dbg(hdev, "hcon %p", conn);
+
+ cp = kmalloc_obj(*cp);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->rand = rand;
+ cp->ediv = ediv;
+ memcpy(cp->ltk, ltk, key_size);
+
+ return hci_cmd_sync_queue_once(hdev, hci_le_enable_encrypt_sync, cp,
+ le_enable_encrypt_complete);
+}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index b7015e37e1f1..0dc236cd4d1c 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -987,7 +987,7 @@ static u8 smp_random(struct smp_chan *smp)
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
return SMP_UNSPECIFIED;
- hci_le_start_enc(hcon, ediv, rand, stk, smp->enc_key_size);
+ hci_le_enable_encrypt(hcon, ediv, rand, stk, smp->enc_key_size);
hcon->enc_key_size = smp->enc_key_size;
set_bit(HCI_CONN_STK_ENCRYPT, hcon->flags);
} else {
@@ -2224,7 +2224,8 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags))
goto unlock;
- hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size);
+ hci_le_enable_encrypt(hcon, key->ediv, key->rand, key->val,
+ key->enc_size);
hcon->enc_key_size = key->enc_size;
/* We never store STKs for initiator role, so clear this flag */
@@ -2927,7 +2928,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
sc_add_ltk(smp);
if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) {
- hci_le_start_enc(hcon, 0, 0, smp->tk, smp->enc_key_size);
+ hci_le_enable_encrypt(hcon, 0, 0, smp->tk, smp->enc_key_size);
hcon->enc_key_size = smp->enc_key_size;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v1 4/6] Bluetooth: L2CAP: Add MIC length validation for HDT PHY
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
` (3 preceding siblings ...)
2026-11-03 22:19 ` [PATCH v1 3/6] Bluetooth: Add initial support for EKS Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 5/6] Bluetooth: Add HCI_OP_LE_SET_HDT_DEFAULT_PARAMS support Luiz Augusto von Dentz
` (2 subsequent siblings)
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add a mic_len field to struct bt_security so userspace can require a
minimum MIC length on an L2CAP channel with BT_SECURITY, and read the
MIC length in use on the link back. The field is added at the end of
the struct, so the shorter struct is still accepted, leaving mic_len
unset.
The requirement is propagated to the connection so LE Enable Encryption
v2 asks for it, and the MIC length reported by Encryption Change v3 is
stored on the connection. When a connection is encrypted on the LE HDT
PHY with a MIC shorter than the channel requires, the LE Credit Based
and Enhanced Credit Based connection requests are rejected with MIC
Length Too Short.
The negotiated MIC length only applies on the LE HDT PHY, the MIC is
always 32 bits on the GFSK PHYs, so hci_conn_mic_len() reports it as
such both for the check and for BT_SECURITY.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/bluetooth.h | 1 +
include/net/bluetooth/hci_core.h | 9 +++++++++
include/net/bluetooth/l2cap.h | 2 ++
net/bluetooth/hci_event.c | 29 +++++++++++++++++----------
net/bluetooth/hci_sync.c | 3 ++-
net/bluetooth/l2cap_core.c | 33 ++++++++++++++++++++++++++++++-
net/bluetooth/l2cap_sock.c | 16 ++++++++++++---
7 files changed, 78 insertions(+), 15 deletions(-)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 945c0bc58a95..9e462c58c4ce 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -66,6 +66,7 @@
struct bt_security {
__u8 level;
__u8 key_size;
+ __u8 mic_len;
};
#define BT_SECURITY_SDP 0
#define BT_SECURITY_LOW 1
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 60318740ec99..d1c6ae23714d 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -765,6 +765,7 @@ struct hci_conn {
__u8 pending_sec_level;
__u8 pin_length;
__u8 enc_key_size;
+ __u8 mic_len;
__u8 io_capability;
__u32 passkey_notify;
__u8 passkey_entered;
@@ -2181,6 +2182,14 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
((conn)->le_tx_def_phys & HCI_LE_SET_PHY_HDT)
#define hdt_rx_capable(conn) \
((conn)->le_rx_def_phys & HCI_LE_SET_PHY_HDT)
+
+/* The MIC length negotiated with LE Enable Encryption v2 only applies on the
+ * LE HDT PHY, the MIC is always 32 bits (0x00) on the GFSK PHYs.
+ */
+static inline __u8 hci_conn_mic_len(struct hci_conn *conn)
+{
+ return conn->le_rx_phy == 0x05 ? conn->mic_len : 0x00;
+}
#define eks_capable(dev) \
(le_feature(dev, 1, 1) & HCI_LE_EKS_FEATURE)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 54eddcdd341f..182df5c5731e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -285,6 +285,7 @@ struct l2cap_conn_rsp {
#define L2CAP_CR_LE_SCID_IN_USE 0x000A
#define L2CAP_CR_LE_UNACCEPT_PARAMS 0x000B
#define L2CAP_CR_LE_INVALID_PARAMS 0x000C
+#define L2CAP_CR_LE_MIC_LEN_TOO_SHORT 0x00FE
/* connect/create channel status */
#define L2CAP_CS_NO_INFO 0x0000
@@ -533,6 +534,7 @@ struct l2cap_chan {
__u8 chan_policy;
__u8 sec_level;
+ __u8 mic_len;
__u8 ident;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 896a2ebd806d..be8b480e6ab7 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3725,7 +3725,8 @@ static void hci_remote_name_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
-static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt)
+static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt,
+ u8 key_size, u8 mic_len)
{
struct hci_dev *hdev = conn->hdev;
@@ -3744,14 +3745,21 @@ static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt)
conn->type == LE_LINK)
set_bit(HCI_CONN_AES_CCM, conn->flags);
+ if (key_size)
+ conn->enc_key_size = key_size;
+
+ if (mic_len)
+ conn->mic_len = mic_len;
+
/* For ACL links the encryption key size is still
- * validated below via hci_read_enc_key_size(). Its
- * completion handler, hci_cc_read_enc_key_size(),
- * reports the final security level so userspace does
- * not see a transient "encrypted" state that a
- * key-size downgrade would immediately invalidate.
+ * validated below via hci_read_enc_key_size(), unless
+ * the event already carried it. Its completion
+ * handler, hci_cc_read_enc_key_size(), reports the
+ * final security level so userspace does not see a
+ * transient "encrypted" state that a key-size
+ * downgrade would immediately invalidate.
*/
- if (conn->type != ACL_LINK)
+ if (conn->type != ACL_LINK || key_size)
mgmt_security_level_changed(conn);
} else {
clear_bit(HCI_CONN_ENCRYPT, conn->flags);
@@ -3796,7 +3804,7 @@ static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt)
}
/* Try reading the encryption key size for encrypted ACL links */
- if (!status && encrypt && conn->type == ACL_LINK) {
+ if (!status && encrypt && conn->type == ACL_LINK && !key_size) {
if (hci_read_enc_key_size(hdev, conn)) {
/* Could not even issue the key-size read: report the
* security level now since hci_cc_read_enc_key_size()
@@ -3853,7 +3861,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
if (!conn)
goto unlock;
- hci_change_encrypt(conn, ev->status, ev->encrypt);
+ hci_change_encrypt(conn, ev->status, ev->encrypt, 0, 0);
unlock:
hci_dev_unlock(hdev);
@@ -3873,7 +3881,8 @@ static void hci_encrypt_change_v3_evt(struct hci_dev *hdev, void *data,
if (!conn)
goto unlock;
- hci_change_encrypt(conn, ev->status, ev->mode);
+ hci_change_encrypt(conn, ev->status, ev->mode, ev->key_size,
+ ev->mic_len);
unlock:
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 1c52b914c236..5f743cf362e5 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -8636,7 +8636,8 @@ static int hci_le_enable_encrypt_v2(struct hci_conn *conn, __le16 ediv,
cp->handle = cpu_to_le16(conn->handle);
cp->rand = rand;
cp->ediv = ediv;
- cp->mic_len = 0x02; /* Always prefer 128 bits? */
+ /* Use requested mic_len from hcon, default to 128 bits if unset */
+ cp->mic_len = conn->mic_len ? conn->mic_len : 0x02;
if (ltk) {
memcpy(cp->ltk, ltk, sizeof(cp->ltk));
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 70ad30b091e4..4abb4b28ddec 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -579,6 +579,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
chan->tx_win = pchan->tx_win;
chan->tx_win_max = pchan->tx_win_max;
chan->sec_level = pchan->sec_level;
+ chan->mic_len = pchan->mic_len;
chan->conf_state = pchan->conf_state;
chan->flags = pchan->flags;
chan->tx_credits = pchan->tx_credits;
@@ -1031,8 +1032,13 @@ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
- if (conn->hcon->type == LE_LINK)
+ if (conn->hcon->type == LE_LINK) {
+ /* Propagate mic_len requirement to hcon for encrypt_v2 */
+ if (chan->mic_len > conn->hcon->mic_len)
+ conn->hcon->mic_len = chan->mic_len;
+
return smp_conn_security(conn->hcon, chan->sec_level);
+ }
auth_type = l2cap_get_auth_type(chan);
@@ -1579,6 +1585,18 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
hcon->enc_key_size >= min_key_size);
}
+static bool l2cap_check_mic_len(struct hci_conn *hcon, struct l2cap_chan *chan)
+{
+ /* If the channel has no MIC length requirement, skip check */
+ if (!chan->mic_len)
+ return true;
+
+ if (!test_bit(HCI_CONN_ENCRYPT, hcon->flags))
+ return true;
+
+ return hci_conn_mic_len(hcon) >= chan->mic_len;
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
__must_hold(&chan->lock)
__must_hold(&chan->conn->lock)
@@ -5283,6 +5301,13 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}
+ /* Check if MIC length is sufficient for HDT PHY */
+ if (!l2cap_check_mic_len(conn->hcon, pchan)) {
+ result = L2CAP_CR_LE_MIC_LEN_TOO_SHORT;
+ chan = NULL;
+ goto response_unlock;
+ }
+
/* Check for valid dynamic CID range */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
result = L2CAP_CR_LE_INVALID_SCID;
@@ -5501,6 +5526,12 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
goto unlock;
}
+ /* Check if MIC length is sufficient */
+ if (!l2cap_check_mic_len(conn->hcon, pchan)) {
+ result = L2CAP_CR_LE_MIC_LEN_TOO_SHORT;
+ goto unlock;
+ }
+
/* Check if the listening channel has set an output MTU then the
* requested MTU shall be less than or equal to that value.
*/
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 7a2551ef7f50..9b7798961266 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -623,8 +623,10 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
if (conn) {
sec.level = conn->hcon->sec_level;
- if (sk->sk_state == BT_CONNECTED)
+ if (sk->sk_state == BT_CONNECTED) {
sec.key_size = conn->hcon->enc_key_size;
+ sec.mic_len = hci_conn_mic_len(conn->hcon);
+ }
} else {
sec.level = chan->sec_level;
}
@@ -960,9 +962,16 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
- sec.level = BT_SECURITY_LOW;
+ /* mic_len was added at the end of struct bt_security, so
+ * accept the shorter struct as well, leaving mic_len unset.
+ */
+ if (optlen < offsetofend(struct bt_security, key_size)) {
+ err = -EINVAL;
+ break;
+ }
- err = copy_safe_from_sockptr(&sec, sizeof(sec), optval, optlen);
+ err = copy_struct_from_sockptr(&sec, sizeof(sec), optval,
+ optlen);
if (err)
break;
@@ -973,6 +982,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
}
chan->sec_level = sec.level;
+ chan->mic_len = sec.mic_len;
conn = l2cap_chan_conn(chan);
if (!conn)
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v1 5/6] Bluetooth: Add HCI_OP_LE_SET_HDT_DEFAULT_PARAMS support
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
` (4 preceding siblings ...)
2026-11-03 22:19 ` [PATCH v1 4/6] Bluetooth: L2CAP: Add MIC length validation for HDT PHY Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-03 22:19 ` [PATCH v1 6/6] Bluetooth: Add EKS Opportunistic Encryption support Luiz Augusto von Dentz
2026-11-04 1:48 ` [GIT,PULL] bluetooth 2026-11-02 bluez.test.bot
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add support for LE Set HDT Default Parameters, which sets the MIC
length, packet format and ACL rates the controller prefers on the LE
HDT PHY. The command is sent during controller initialization when the
controller supports HDT and the command, with the rates limited to the
ones the controller supports.
The defaults are stored in hci_dev, 128-bit MIC, no packet format
preference and all HDT rates, and exposed through the MGMT system
configuration TLVs 0x0021-0x0023 so userspace can change them; changing
them while the controller is up sends the command again.
The default MIC length also applies when a connection did not request
one: to LE Enable Encryption v2, LE Set CIG Parameters v2 and LE Create
BIG v2, and to the L2CAP channels, so ACL, CIS and BIS are set up with
the MIC length the host was told to use.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/hci.h | 7 +++++
include/net/bluetooth/hci_core.h | 3 +++
net/bluetooth/hci_core.c | 3 +++
net/bluetooth/hci_sync.c | 37 ++++++++++++++++++++++++---
net/bluetooth/l2cap_core.c | 19 +++++++++-----
net/bluetooth/mgmt_config.c | 44 ++++++++++++++++++++++++++++++++
6 files changed, 104 insertions(+), 9 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index e9bbd687b12e..af8a20682e31 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -2664,6 +2664,13 @@ struct hci_cp_le_create_big_v2 {
struct hci_bis_v2 bis;
} __packed;
+#define HCI_OP_LE_SET_HDT_DEFAULT_PARAMS 0x20bc
+struct hci_cp_le_set_hdt_default_params {
+ __u8 mic_len;
+ __u8 format;
+ __le16 rates;
+} __packed;
+
/* ---- HCI Events ---- */
struct hci_ev_status {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d1c6ae23714d..def547e1b7f1 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -433,6 +433,9 @@ struct hci_dev {
__u8 hdt_max_tx_win;
__u8 hdt_max_rx_win;
__u8 hdt_max_rx_packet;
+ __u8 hdt_def_mic_len;
+ __u8 hdt_def_format;
+ __u16 hdt_def_rates;
__u8 le_max_key_size;
__u8 le_min_key_size;
__u16 discov_interleaved_timeout;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index bfc5bdebc71a..a4014bbcdfc2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2575,6 +2575,9 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
hdev->le_min_key_size = SMP_MIN_ENC_KEY_SIZE;
hdev->le_tx_def_phys = HCI_LE_SET_PHY_1M;
hdev->le_rx_def_phys = HCI_LE_SET_PHY_1M;
+ hdev->hdt_def_mic_len = 0x02; /* 128-bit */
+ hdev->hdt_def_format = 0x00; /* No preference */
+ hdev->hdt_def_rates = 0x001f; /* All defined HDT rates */
hdev->le_num_of_adv_sets = HCI_MAX_ADV_INSTANCES;
hdev->def_multi_adv_rotation_duration = HCI_DEFAULT_ADV_DURATION;
hdev->def_le_autoconnect_timeout = HCI_LE_CONN_TIMEOUT;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 5f743cf362e5..b2b1a553e7c2 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5412,11 +5412,40 @@ static int hci_le_set_default_phy_sync(struct hci_dev *hdev)
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}
+static int hci_le_set_hdt_params_sync(struct hci_dev *hdev)
+{
+ struct hci_cp_le_set_hdt_default_params cp;
+ u16 rates;
+
+ /* Only send if controller supports HDT and the command */
+ if (!hdt_capable(hdev) || !(hdev->commands[52] & BIT(1)))
+ return 0;
+
+ /* Compute supported rates based on controller capabilities */
+ rates = hdev->hdt_def_rates;
+
+ /* Mask out rates not supported by the controller */
+ if (!hdt_6_tx_capable(hdev))
+ rates &= ~BIT(3);
+ if (!hdt_7_5_tx_capable(hdev))
+ rates &= ~BIT(4);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mic_len = hdev->hdt_def_mic_len;
+ cp.format = hdev->hdt_def_format;
+ cp.rates = cpu_to_le16(rates);
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HDT_DEFAULT_PARAMS,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
static const struct hci_init_stage le_init4[] = {
/* HCI_OP_LE_WRITE_DEF_DATA_LEN */
HCI_INIT(hci_le_set_write_def_data_len_sync),
/* HCI_OP_LE_SET_DEFAULT_PHY */
HCI_INIT(hci_le_set_default_phy_sync),
+ /* HCI_OP_LE_SET_HDT_DEFAULT_PARAMS */
+ HCI_INIT(hci_le_set_hdt_params_sync),
{}
};
@@ -8431,7 +8460,8 @@ static int hci_le_set_cig_params_v2_sync(struct hci_dev *hdev, u8 cig_id)
qos->ucast.in.rates;
cis->p_hdt_rates = qos->ucast.in.rates ? qos->ucast.in.rates :
qos->ucast.out.rates;
- cis->mic_len = 0x02; /* Always prefer 128 bits? */
+ cis->mic_len = conn->mic_len ? conn->mic_len :
+ hdev->hdt_def_mic_len;
/* If HDT PHY is set but rates are not set then enable all HDT
* supported rates.
@@ -8559,6 +8589,7 @@ static int hci_le_create_big_v2_sync(struct hci_conn *conn,
cp.bis.encryption = qos->bcast.encryption;
memcpy(cp.bis.bcode, qos->bcast.bcode, sizeof(cp.bis.bcode));
cp.bis.hdt_rates = qos->bcast.out.rates ? qos->bcast.out.rates : 0x00;
+ cp.bis.mic_len = conn->mic_len ? conn->mic_len : hdev->hdt_def_mic_len;
/* If HDT PHY is set but rates are not set then enable all HDT
* supported rates.
@@ -8636,8 +8667,8 @@ static int hci_le_enable_encrypt_v2(struct hci_conn *conn, __le16 ediv,
cp->handle = cpu_to_le16(conn->handle);
cp->rand = rand;
cp->ediv = ediv;
- /* Use requested mic_len from hcon, default to 128 bits if unset */
- cp->mic_len = conn->mic_len ? conn->mic_len : 0x02;
+ /* Use requested mic_len from hcon if set; otherwise use hdev default */
+ cp->mic_len = conn->mic_len ? conn->mic_len : hdev->hdt_def_mic_len;
if (ltk) {
memcpy(cp->ltk, ltk, sizeof(cp->ltk));
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4abb4b28ddec..73712db05e14 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1030,19 +1030,26 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
{
struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
__u8 auth_type;
- if (conn->hcon->type == LE_LINK) {
- /* Propagate mic_len requirement to hcon for encrypt_v2 */
- if (chan->mic_len > conn->hcon->mic_len)
- conn->hcon->mic_len = chan->mic_len;
+ if (hcon->type == LE_LINK) {
+ /* Propagate mic_len requirement to hcon for encrypt_v2.
+ * Use chan->mic_len if set, otherwise fall back to the
+ * device default HDT MIC length.
+ */
+ u8 mic_len = chan->mic_len ? chan->mic_len :
+ hcon->hdev->hdt_def_mic_len;
+
+ if (mic_len > hcon->mic_len)
+ hcon->mic_len = mic_len;
- return smp_conn_security(conn->hcon, chan->sec_level);
+ return smp_conn_security(hcon, chan->sec_level);
}
auth_type = l2cap_get_auth_type(chan);
- return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
+ return hci_conn_security(hcon, chan->sec_level, auth_type,
initiator);
}
diff --git a/net/bluetooth/mgmt_config.c b/net/bluetooth/mgmt_config.c
index fdcc752c6f13..f5b37b0cd975 100644
--- a/net/bluetooth/mgmt_config.c
+++ b/net/bluetooth/mgmt_config.c
@@ -91,6 +91,9 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
HDEV_PARAM_U16(advmon_no_filter_duration);
HDEV_PARAM_U8(enable_advmon_interleave_scan);
HDEV_PARAM_U32(idle_timeout);
+ HDEV_PARAM_U8(hdt_def_mic_len);
+ HDEV_PARAM_U8(hdt_def_format);
+ HDEV_PARAM_U16(hdt_def_rates);
} __packed rp = {
TLV_SET_U16(0x0000, def_page_scan_type),
TLV_SET_U16(0x0001, def_page_scan_int),
@@ -125,6 +128,9 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
TLV_SET_U16(0x001e, advmon_no_filter_duration),
TLV_SET_U8(0x001f, enable_advmon_interleave_scan),
TLV_SET_U32(0x0020, idle_timeout),
+ TLV_SET_U8(0x0021, hdt_def_mic_len),
+ TLV_SET_U8(0x0022, hdt_def_format),
+ TLV_SET_U16(0x0023, hdt_def_rates),
};
bt_dev_dbg(hdev, "sock %p", sk);
@@ -140,11 +146,30 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
#define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value)))
#define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value)))
+static int hci_le_set_hdt_params_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_set_hdt_default_params cp;
+
+ if (!hdt_capable(hdev) || !(hdev->commands[52] & BIT(1)))
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mic_len = hdev->hdt_def_mic_len;
+ cp.format = hdev->hdt_def_format;
+ cp.rates = cpu_to_le16(hdev->hdt_def_rates);
+
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HDT_DEFAULT_PARAMS,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+}
+
int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len)
{
u16 buffer_left = data_len;
u8 *buffer = data;
+ u8 old_mic_len = hdev->hdt_def_mic_len;
+ u8 old_format = hdev->hdt_def_format;
+ u16 old_rates = hdev->hdt_def_rates;
if (buffer_left < sizeof(struct mgmt_tlv)) {
return mgmt_cmd_status(sk, hdev->id,
@@ -201,9 +226,12 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
case 0x001b:
case 0x001d:
case 0x001e:
+ case 0x0023:
exp_type_len = sizeof(u16);
break;
case 0x001f:
+ case 0x0021:
+ case 0x0022:
exp_type_len = sizeof(u8);
break;
case 0x0020:
@@ -335,6 +363,15 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
case 0x00020:
hdev->idle_timeout = TLV_GET_LE32(buffer);
break;
+ case 0x0021:
+ hdev->hdt_def_mic_len = TLV_GET_U8(buffer);
+ break;
+ case 0x0022:
+ hdev->hdt_def_format = TLV_GET_U8(buffer);
+ break;
+ case 0x0023:
+ hdev->hdt_def_rates = TLV_GET_LE16(buffer);
+ break;
default:
bt_dev_warn(hdev, "unsupported parameter %u", type);
break;
@@ -344,6 +381,13 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
buffer += exp_len;
}
+ if (hci_dev_test_flag(hdev, HCI_UP) &&
+ (hdev->hdt_def_mic_len != old_mic_len ||
+ hdev->hdt_def_format != old_format ||
+ hdev->hdt_def_rates != old_rates))
+ hci_cmd_sync_queue(hdev, hci_le_set_hdt_params_sync,
+ NULL, NULL);
+
return mgmt_cmd_complete(sk, hdev->id,
MGMT_OP_SET_DEF_SYSTEM_CONFIG, 0, NULL, 0);
}
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v1 6/6] Bluetooth: Add EKS Opportunistic Encryption support
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
` (5 preceding siblings ...)
2026-11-03 22:19 ` [PATCH v1 5/6] Bluetooth: Add HCI_OP_LE_SET_HDT_DEFAULT_PARAMS support Luiz Augusto von Dentz
@ 2026-11-03 22:19 ` Luiz Augusto von Dentz
2026-11-04 1:48 ` [GIT,PULL] bluetooth 2026-11-02 bluez.test.bot
7 siblings, 0 replies; 14+ messages in thread
From: Luiz Augusto von Dentz @ 2026-11-03 22:19 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Add support for opportunistic encryption with Encryption Key Schedule,
which encrypts a link without pairing when both devices support it:
- The Opportunistic Encryption (Host Support) feature bit (Link Layer
feature bit 90) is set with LE Set Host Feature during
initialization, and eks_oppt_capable() checks the remote supports it.
- When the remote features are read as Central and there is no LTK for
the peer, LE Enable Encryption v2 is sent with Encryption set to
opportunistic. The checks are repeated right before the command is
sent, so it is skipped if the link is gone, already encrypted or SMP
pairing has started in the meantime, since encrypting the link under
SMP would break the pairing.
- An opportunistically encrypted link does not raise the security
level, as it is not authenticated.
- The feature can be turned off with the MGMT system configuration TLV
0x0024; it is on by default.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 5 ++
include/net/bluetooth/hci_sync.h | 1 +
net/bluetooth/hci_core.c | 1 +
net/bluetooth/hci_event.c | 7 ++-
net/bluetooth/hci_sync.c | 92 +++++++++++++++++++++++++++++++-
net/bluetooth/mgmt_config.c | 5 ++
net/bluetooth/smp.c | 23 ++++++++
net/bluetooth/smp.h | 2 +
9 files changed, 135 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index af8a20682e31..bcd2f5aa1987 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -668,6 +668,7 @@ enum {
#define HCI_LE_HDT_7_5_TX_FEATURE BIT(1)
#define HCI_LE_HDT_PF_1 BIT(2)
#define HCI_LE_HDT_LQF_FEATURE BIT(3)
+#define HCI_LE_OPPORTUNISTIC_ENC BIT(2) /* Byte 3 */
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index def547e1b7f1..fe43a968eabe 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -436,6 +436,7 @@ struct hci_dev {
__u8 hdt_def_mic_len;
__u8 hdt_def_format;
__u16 hdt_def_rates;
+ bool eks_oppt;
__u8 le_max_key_size;
__u8 le_min_key_size;
__u16 discov_interleaved_timeout;
@@ -2196,6 +2197,10 @@ static inline __u8 hci_conn_mic_len(struct hci_conn *conn)
#define eks_capable(dev) \
(le_feature(dev, 1, 1) & HCI_LE_EKS_FEATURE)
+#define eks_oppt_capable(dev) \
+ (eks_capable(dev) && \
+ (le_feature(dev, 1, 3) & HCI_LE_OPPORTUNISTIC_ENC))
+
/* Channel sounding support */
#define le_cs_capable(dev) \
((dev)->le_features[5] & HCI_LE_CS)
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index 4b5baee9ce11..5f74af68730b 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -200,3 +200,4 @@ int hci_le_create_big_sync(struct hci_conn *conn, struct bt_iso_qos_v2 *qos,
int hci_le_enable_encrypt(struct hci_conn *conn, __le16 ediv, __le64 rand,
__u8 ltk[16], __u8 key_size);
+int hci_le_eks_oppt_encrypt(struct hci_conn *conn);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index a4014bbcdfc2..06e676eed468 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2578,6 +2578,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
hdev->hdt_def_mic_len = 0x02; /* 128-bit */
hdev->hdt_def_format = 0x00; /* No preference */
hdev->hdt_def_rates = 0x001f; /* All defined HDT rates */
+ hdev->eks_oppt = true; /* Opportunistic encryption enabled */
hdev->le_num_of_adv_sets = HCI_MAX_ADV_INSTANCES;
hdev->def_multi_adv_rotation_duration = HCI_DEFAULT_ADV_DURATION;
hdev->def_le_autoconnect_timeout = HCI_LE_CONN_TIMEOUT;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index be8b480e6ab7..4037ee6cf885 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3732,7 +3732,6 @@ static void hci_change_encrypt(struct hci_conn *conn, u8 status, u8 encrypt,
if (!status) {
if (encrypt) {
- /* Encryption implies authentication */
set_bit(HCI_CONN_AUTH, conn->flags);
set_bit(HCI_CONN_ENCRYPT, conn->flags);
conn->sec_level = conn->pending_sec_level;
@@ -3881,6 +3880,10 @@ static void hci_encrypt_change_v3_evt(struct hci_dev *hdev, void *data,
if (!conn)
goto unlock;
+ /* EKS Opportunistic (eks_mode 0x02) provides no security change */
+ if (ev->eks_mode == 0x02)
+ goto unlock;
+
hci_change_encrypt(conn, ev->status, ev->mode, ev->key_size,
ev->mic_len);
@@ -7867,6 +7870,8 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_HDT;
conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_HDT;
}
+
+ hci_le_eks_oppt_encrypt(conn);
}
if (conn->state == BT_CONFIG) {
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index b2b1a553e7c2..b03c09466f8b 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5053,9 +5053,18 @@ static int hci_le_set_host_features_sync(struct hci_dev *hdev)
return err;
}
- if (le_sci_capable(hdev))
+ if (le_sci_capable(hdev)) {
/* Shorter Connection Intervals (Host Support) */
err = hci_le_set_host_feature_sync(hdev, 73, 0x01);
+ if (err)
+ return err;
+ }
+
+ if (eks_capable(hdev))
+ /* Opportunistic Encryption (Host Support) */
+ err = hci_le_set_host_feature_sync(hdev, 90,
+ (hdev->eks_oppt ? 0x01 :
+ 0x00));
return err;
}
@@ -8714,3 +8723,84 @@ int hci_le_enable_encrypt(struct hci_conn *conn, __le16 ediv, __le64 rand,
return hci_cmd_sync_queue_once(hdev, hci_le_enable_encrypt_sync, cp,
le_enable_encrypt_complete);
}
+
+/* The opportunistic encryption is queued when the connection is set up, so
+ * by the time it runs the connection may be gone, already encrypted or in
+ * the middle of SMP pairing. Starting it under pairing would encrypt the
+ * link while SMP is still exchanging PDUs and break the pairing, so check
+ * again right before sending the command and leave the link to SMP.
+ */
+static int hci_le_eks_oppt_encrypt_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_enable_encrypt_v2 *cp = data;
+ struct hci_conn *conn;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, le16_to_cpu(cp->handle));
+ if (!conn || conn->state != BT_CONNECTED ||
+ test_bit(HCI_CONN_ENCRYPT, conn->flags))
+ goto unlock;
+
+ if (smp_pairing_in_progress(conn)) {
+ bt_dev_dbg(hdev, "SMP in progress, skipping hcon %p", conn);
+ goto unlock;
+ }
+
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
+ goto unlock;
+
+ bt_dev_dbg(hdev, "starting EKS opportunistic encryption hcon %p", conn);
+
+ hci_dev_unlock(hdev);
+
+ return hci_le_enable_encrypt_v2_sync(hdev, data);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return 0;
+}
+
+int hci_le_eks_oppt_encrypt(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_enable_encrypt_v2 *cp;
+
+ /* Only central initiates encryption */
+ if (conn->role != HCI_ROLE_MASTER)
+ return -EINVAL;
+
+ /* Check hdev config */
+ if (!hdev->eks_oppt)
+ return -EOPNOTSUPP;
+
+ /* Remote must support opportunistic encryption */
+ if (!eks_oppt_capable(conn))
+ return -EOPNOTSUPP;
+
+ /* Must support encrypt_v2 command */
+ if (!(hdev->commands[50] & BIT(4)))
+ return -EOPNOTSUPP;
+
+ /* Don't encrypt if already encrypted or pending */
+ if (test_bit(HCI_CONN_ENCRYPT, conn->flags) ||
+ test_bit(HCI_CONN_ENCRYPT_PEND, conn->flags))
+ return -EALREADY;
+
+ /* Skip if we have an LTK for this peer (use normal encryption) */
+ if (hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role))
+ return -EEXIST;
+
+ bt_dev_dbg(hdev, "queueing EKS opportunistic encryption hcon %p", conn);
+
+ cp = kzalloc_obj(*cp);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->mic_len = conn->mic_len ? conn->mic_len : hdev->hdt_def_mic_len;
+ cp->encryption = 0x01; /* Opportunistic with EKS */
+
+ return hci_cmd_sync_queue_once(hdev, hci_le_eks_oppt_encrypt_sync, cp,
+ le_enable_encrypt_complete);
+}
diff --git a/net/bluetooth/mgmt_config.c b/net/bluetooth/mgmt_config.c
index f5b37b0cd975..1b9b823355b4 100644
--- a/net/bluetooth/mgmt_config.c
+++ b/net/bluetooth/mgmt_config.c
@@ -94,6 +94,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
HDEV_PARAM_U8(hdt_def_mic_len);
HDEV_PARAM_U8(hdt_def_format);
HDEV_PARAM_U16(hdt_def_rates);
+ HDEV_PARAM_U8(eks_oppt);
} __packed rp = {
TLV_SET_U16(0x0000, def_page_scan_type),
TLV_SET_U16(0x0001, def_page_scan_int),
@@ -131,6 +132,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
TLV_SET_U8(0x0021, hdt_def_mic_len),
TLV_SET_U8(0x0022, hdt_def_format),
TLV_SET_U16(0x0023, hdt_def_rates),
+ TLV_SET_U8(0x0024, eks_oppt),
};
bt_dev_dbg(hdev, "sock %p", sk);
@@ -372,6 +374,9 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
case 0x0023:
hdev->hdt_def_rates = TLV_GET_LE16(buffer);
break;
+ case 0x0024:
+ hdev->eks_oppt = !!TLV_GET_U8(buffer);
+ break;
default:
bt_dev_warn(hdev, "unsupported parameter %u", type);
break;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 0dc236cd4d1c..112100e790cd 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -2444,6 +2444,29 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
return ret;
}
+bool smp_pairing_in_progress(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn;
+ struct l2cap_chan *chan;
+ bool pending;
+
+ lockdep_assert_held(&hcon->hdev->lock);
+
+ conn = hcon->l2cap_data;
+ if (!conn)
+ return false;
+
+ chan = conn->smp;
+ if (!chan)
+ return false;
+
+ l2cap_chan_lock(chan);
+ pending = !!chan->data;
+ l2cap_chan_unlock(chan);
+
+ return pending;
+}
+
int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type)
{
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index c86c46389007..0537762e2775 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -185,6 +185,8 @@ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
enum smp_key_pref key_pref);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
+bool smp_pairing_in_progress(struct hci_conn *hcon)
+ __must_hold(&hcon->hdev->lock);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey)
__must_hold(&conn->hdev->lock);
--
2.56.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* RE: [GIT,PULL] bluetooth 2026-11-02
2026-11-03 22:19 [GIT PULL] bluetooth 2026-11-02 Luiz Augusto von Dentz
` (6 preceding siblings ...)
2026-11-03 22:19 ` [PATCH v1 6/6] Bluetooth: Add EKS Opportunistic Encryption support Luiz Augusto von Dentz
@ 2026-11-04 1:48 ` bluez.test.bot
7 siblings, 0 replies; 14+ messages in thread
From: bluez.test.bot @ 2026-11-04 1:48 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
[-- Attachment #1: Type: text/plain, Size: 753 bytes --]
This is an automated email and please do not reply to this email.
Dear Submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.
----- Output -----
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To record the empty patch as an empty commit, run "git am --allow-empty".
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Please resolve the issue and submit the patches again.
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 14+ messages in thread