diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ede7a153c69a5f4b5330885a15352b8f7c6c0b81..e6dfbeb4f66348c1955e83671e2928394d5bcb5d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1430,6 +1430,15 @@ struct hci_cp_le_set_event_mask { __u8 mask[8]; } __packed; +/* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E + * 7.8.2 LE Read Buffer Size command + * MAX_LE_MTU is 0xffff. + * 0 is also valid. It means that no dedicated LE Buffer exists. + * It should use the HCI_Read_Buffer_Size command and mtu is shared + * between BR/EDR and LE. + */ +#define HCI_MIN_LE_MTU 0x001b + #define HCI_OP_LE_READ_BUFFER_SIZE 0x2002 struct hci_rp_le_read_buffer_size { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 33873266b2bc7cb2bad783390b215ee9b2a80576..202367e98eea78ed7658d69472113cf6bd09f595 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -607,6 +607,7 @@ struct hci_conn { __u8 resp_addr_type; __u16 handle; __u16 state; + __u16 mtu; __u8 mode; __u8 type; __u8 role; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 52e512f41da3c812f567d11360170423d75b3df1..de1e47b70d0e555c83c89592a3907c8eaeebced8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -520,6 +520,27 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, { struct hci_conn *conn; + switch (type) { + case ACL_LINK: + if (!hdev->acl_mtu) + return NULL; + break; + case LE_LINK: + if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU) + return NULL; + if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU) + return NULL; + break; + case SCO_LINK: + case ESCO_LINK: + if (!hdev->sco_pkts) + /* Controller does not support SCO or eSCO over HCI */ + return NULL; + break; + default: + return NULL; + } + BT_DBG("%s dst %pMR", hdev->name, dst); conn = kzalloc(sizeof(*conn), GFP_KERNEL); @@ -553,10 +574,12 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; + conn->mtu = hdev->acl_mtu; break; case LE_LINK: /* conn->src should reflect the local identity address */ hci_copy_identity_address(hdev, &conn->src, &conn->src_type); + conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; break; case SCO_LINK: if (lmp_esco_capable(hdev)) @@ -564,9 +587,12 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, (hdev->esco_type & EDR_ESCO_MASK); else conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; + + conn->mtu = hdev->sco_mtu; break; case ESCO_LINK: conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; + conn->mtu = hdev->sco_mtu; break; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4027c79786fd336d1853e50668ef50e898050615..e01159d8fabf0791bd906150849044994bbd8095 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -731,6 +731,10 @@ static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; + if (!__le16_to_cpu(rp->acl_mtu) || + !__le16_to_cpu(rp->acl_max_pkt)) + return; + hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu); hdev->sco_mtu = rp->sco_mtu; hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt); @@ -1020,6 +1024,10 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, if (rp->status) return; + if (__le16_to_cpu(rp->le_mtu) && + __le16_to_cpu(rp->le_mtu) < HCI_MIN_LE_MTU) + return; + hdev->le_mtu = __le16_to_cpu(rp->le_mtu); hdev->le_pkts = rp->le_max_pkt; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8ac9adb2b28b0059c325fb0497dbb4ad2a082bcc..6f93e2a7bf91b72a4db8bf6e50df7fade436b6f5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7241,7 +7241,7 @@ static int l2cap_finish_move(struct l2cap_chan *chan) if (chan->hs_hcon) chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; else - chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + chan->conn->mtu = chan->conn->hcon->mtu; return l2cap_resegment(chan); } @@ -7312,7 +7312,7 @@ static int l2cap_rx_state_wait_f(struct l2cap_chan *chan, if (chan->hs_hcon) chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; else - chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + chan->conn->mtu = chan->conn->hcon->mtu; err = l2cap_resegment(chan); @@ -7863,18 +7863,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); - switch (hcon->type) { - case LE_LINK: - if (hcon->hdev->le_mtu) { - conn->mtu = hcon->hdev->le_mtu; - break; - } - fallthrough; - default: - conn->mtu = hcon->hdev->acl_mtu; - break; - } - + conn->mtu = hcon->mtu; conn->feat_mask = 0; conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index f9a2993fd4522b99a5712ee32867128200cb4ec7..1e5daa03155f413a05238715addaacd9d14771f7 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -127,7 +127,6 @@ static void sco_sock_clear_timer(struct sock *sk) /* ---- SCO connections ---- */ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) { - struct hci_dev *hdev = hcon->hdev; struct sco_conn *conn = hcon->sco_data; if (conn) @@ -142,9 +141,10 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) hcon->sco_data = conn; conn->hcon = hcon; + conn->mtu = hcon->mtu; - if (hdev->sco_mtu > 0) - conn->mtu = hdev->sco_mtu; + if (hcon->mtu > 0) + conn->mtu = hcon->mtu; else conn->mtu = 60;