summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2025-12-17 10:50:51 -0500
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2026-01-29 13:25:34 -0500
commit132c0779d4a2d08541519cf04783bca52c6ec85c (patch)
tree7fd0b0bb00a790860da9ceebc27c5ca646f20ebf /net/bluetooth
parentfe05e3c0593fda2c476b359b012065a42dccdd71 (diff)
downloadlinux-next-132c0779d4a2d08541519cf04783bca52c6ec85c.tar.gz
linux-next-132c0779d4a2d08541519cf04783bca52c6ec85c.zip
Bluetooth: L2CAP: Add support for setting BT_PHY
This enables client to use setsockopt(BT_PHY) to set the connection packet type/PHY: Example setting BT_PHY_BR_1M_1SLOT: < HCI Command: Change Conne.. (0x01|0x000f) plen 4 Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) Packet type: 0x331e 2-DH1 may not be used 3-DH1 may not be used DM1 may be used DH1 may be used 2-DH3 may not be used 3-DH3 may not be used 2-DH5 may not be used 3-DH5 may not be used > HCI Event: Command Status (0x0f) plen 4 Change Connection Packet Type (0x01|0x000f) ncmd 1 Status: Success (0x00) > HCI Event: Connection Packet Typ.. (0x1d) plen 5 Status: Success (0x00) Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) Packet type: 0x331e 2-DH1 may not be used 3-DH1 may not be used DM1 may be used DH1 may be used 2-DH3 may not be used 3-DH3 may not be used 2-DH5 may not be used Example setting BT_PHY_LE_1M_TX and BT_PHY_LE_1M_RX: < HCI Command: LE Set PHY (0x08|0x0032) plen 7 Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) All PHYs preference: 0x00 TX PHYs preference: 0x01 LE 1M RX PHYs preference: 0x01 LE 1M PHY options preference: Reserved (0x0000) > HCI Event: Command Status (0x0f) plen 4 LE Set PHY (0x08|0x0032) ncmd 1 Status: Success (0x00) > HCI Event: LE Meta Event (0x3e) plen 6 LE PHY Update Complete (0x0c) Status: Success (0x00) Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation) TX PHY: LE 1M (0x01) RX PHY: LE 1M (0x01) Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_conn.c105
-rw-r--r--net/bluetooth/hci_event.c26
-rw-r--r--net/bluetooth/hci_sync.c72
-rw-r--r--net/bluetooth/l2cap_sock.c20
4 files changed, 222 insertions, 1 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index dc085856f5e9..1a4b6badf2b3 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2958,6 +2958,111 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
return phys;
}
+static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys)
+{
+ u16 pkt_type = conn->pkt_type;
+
+ if (phys & BT_PHY_BR_1M_3SLOT)
+ pkt_type |= HCI_DM3 | HCI_DH3;
+ else
+ pkt_type &= ~(HCI_DM3 | HCI_DH3);
+
+ if (phys & BT_PHY_BR_1M_5SLOT)
+ pkt_type |= HCI_DM5 | HCI_DH5;
+ else
+ pkt_type &= ~(HCI_DM5 | HCI_DH5);
+
+ if (phys & BT_PHY_EDR_2M_1SLOT)
+ pkt_type &= ~HCI_2DH1;
+ else
+ pkt_type |= HCI_2DH1;
+
+ if (phys & BT_PHY_EDR_2M_3SLOT)
+ pkt_type &= ~HCI_2DH3;
+ else
+ pkt_type |= HCI_2DH3;
+
+ if (phys & BT_PHY_EDR_2M_5SLOT)
+ pkt_type &= ~HCI_2DH5;
+ else
+ pkt_type |= HCI_2DH5;
+
+ if (phys & BT_PHY_EDR_3M_1SLOT)
+ pkt_type &= ~HCI_3DH1;
+ else
+ pkt_type |= HCI_3DH1;
+
+ if (phys & BT_PHY_EDR_3M_3SLOT)
+ pkt_type &= ~HCI_3DH3;
+ else
+ pkt_type |= HCI_3DH3;
+
+ if (phys & BT_PHY_EDR_3M_5SLOT)
+ pkt_type &= ~HCI_3DH5;
+ else
+ pkt_type |= HCI_3DH5;
+
+ return pkt_type;
+}
+
+static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys)
+{
+ if (!tx_phys || !rx_phys)
+ return -EINVAL;
+
+ *tx_phys = 0;
+ *rx_phys = 0;
+
+ if (phys & BT_PHY_LE_1M_TX)
+ *tx_phys |= HCI_LE_SET_PHY_1M;
+
+ if (phys & BT_PHY_LE_1M_RX)
+ *rx_phys |= HCI_LE_SET_PHY_1M;
+
+ if (phys & BT_PHY_LE_2M_TX)
+ *tx_phys |= HCI_LE_SET_PHY_2M;
+
+ if (phys & BT_PHY_LE_2M_RX)
+ *rx_phys |= HCI_LE_SET_PHY_2M;
+
+ if (phys & BT_PHY_LE_CODED_TX)
+ *tx_phys |= HCI_LE_SET_PHY_CODED;
+
+ if (phys & BT_PHY_LE_CODED_RX)
+ *rx_phys |= HCI_LE_SET_PHY_CODED;
+
+ return 0;
+}
+
+int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
+{
+ u8 tx_phys, rx_phys;
+
+ switch (conn->type) {
+ case SCO_LINK:
+ case ESCO_LINK:
+ return -EINVAL;
+ case ACL_LINK:
+ /* Only allow setting BR/EDR PHYs if link type is ACL */
+ if (phys & ~BT_PHY_BREDR_MASK)
+ return -EINVAL;
+
+ return hci_acl_change_pkt_type(conn,
+ 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 (bt_phy_le_phy(phys, &tx_phys, &rx_phys))
+ return -EINVAL;
+
+ return hci_le_set_phy(conn, tx_phys, rx_phys);
+ default:
+ return -EINVAL;
+ }
+}
+
static int abort_conn_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 58075bf72055..467710a42d45 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2869,6 +2869,31 @@ static void hci_cs_le_ext_create_conn(struct hci_dev *hdev, u8 status)
hci_dev_unlock(hdev);
}
+static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_set_phy *cp;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", status);
+
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PHY);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ conn->le_tx_def_phys = cp->tx_phys;
+ conn->le_rx_def_phys = cp->rx_phys;
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_read_remote_features *cp;
@@ -4359,6 +4384,7 @@ static const struct hci_cs {
HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn),
HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features),
HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc),
+ HCI_CS(HCI_OP_LE_SET_PHY, hci_cs_le_set_phy),
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 4163d9b16e1a..0e6660ddc00a 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -7448,3 +7448,75 @@ int hci_le_read_remote_features(struct hci_conn *conn)
return err;
}
+
+static void pkt_type_changed(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_cp_change_conn_ptype *cp = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(cp);
+}
+
+static int hci_change_conn_ptype_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_change_conn_ptype *cp = data;
+
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_CHANGE_CONN_PTYPE,
+ sizeof(*cp), cp,
+ HCI_EV_PKT_TYPE_CHANGE,
+ HCI_CMD_TIMEOUT, NULL);
+}
+
+int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_change_conn_ptype *cp;
+
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->pkt_type = cpu_to_le16(pkt_type);
+
+ return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
+ pkt_type_changed);
+}
+
+static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_cp_le_set_phy *cp = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(cp);
+}
+
+static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_set_phy *cp = data;
+
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_SET_PHY,
+ sizeof(*cp), cp,
+ HCI_EV_LE_PHY_UPDATE_COMPLETE,
+ HCI_CMD_TIMEOUT, NULL);
+}
+
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_set_phy *cp;
+
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->tx_phys = tx_phys;
+ cp->rx_phys = rx_phys;
+
+ return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
+ le_phy_update_complete);
+}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 9ee189c815d4..3ba3ce7eaa98 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -885,7 +885,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
struct bt_power pwr;
struct l2cap_conn *conn;
int err = 0;
- u32 opt;
+ u32 opt, phys;
u16 mtu;
u8 mode;
@@ -1059,6 +1059,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
+ case BT_PHY:
+ if (sk->sk_state != BT_CONNECTED) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ err = copy_safe_from_sockptr(&phys, sizeof(phys), optval,
+ optlen);
+ if (err)
+ break;
+
+ if (!chan->conn)
+ break;
+
+ conn = chan->conn;
+ err = hci_conn_set_phy(conn->hcon, phys);
+ break;
+
case BT_MODE:
if (!enable_ecred) {
err = -ENOPROTOOPT;