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 11a92bb4d7a9f5ad6f968e429718e54af020ac2a..5d8b6bd87495d09fe3d0ff8621e033819dced6fd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -602,6 +602,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 140d9764c77e3389955bfa1fc39957210b670aec..9478e63f10ef458b8d6d6ce796fde89e3e6842e0 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -522,6 +522,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); @@ -555,10 +576,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)) @@ -566,9 +589,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 954b29605c94249bdc509c45f6cfd56f38ba5985..ea83c75da095215859d92660b5a8da1ca2bef51e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -729,6 +729,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); @@ -1018,6 +1022,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 1677cbb775788b963fa6511d14638edf133d3a01..c3b6a416119e7c5a795ec274bdb349db30334975 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7204,7 +7204,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); } @@ -7275,7 +7275,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); @@ -7826,18 +7826,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;