]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - net/bluetooth/hci_conn.c
Bluetooth: L2CAP: Fix div-by-zero in l2cap_le_flowctl_init()
[thirdparty/linux.git] / net / bluetooth / hci_conn.c
index b7f8e99001dda43b0dc96b0009c08d39283c2760..f21e7c73021d8fde21d5a83c38accc9f4d3df6a1 100644 (file)
@@ -904,11 +904,37 @@ 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 ERR_PTR(-ECONNREFUSED);
+               break;
+       case ISO_LINK:
+               if (hdev->iso_mtu)
+                       /* Dedicated ISO Buffer exists */
+                       break;
+               fallthrough;
+       case LE_LINK:
+               if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
+                       return ERR_PTR(-ECONNREFUSED);
+               if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
+                       return ERR_PTR(-ECONNREFUSED);
+               break;
+       case SCO_LINK:
+       case ESCO_LINK:
+               if (!hdev->sco_pkts)
+                       /* Controller does not support SCO or eSCO over HCI */
+                       return ERR_PTR(-ECONNREFUSED);
+               break;
+       default:
+               return ERR_PTR(-ECONNREFUSED);
+       }
+
        bt_dev_dbg(hdev, "dst %pMR handle 0x%4.4x", dst, handle);
 
        conn = kzalloc(sizeof(*conn), GFP_KERNEL);
        if (!conn)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        bacpy(&conn->dst, dst);
        bacpy(&conn->src, &hdev->bdaddr);
@@ -939,10 +965,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 ISO_LINK:
                /* conn->src should reflect the local identity address */
@@ -954,6 +982,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
                else if (conn->role == HCI_ROLE_MASTER)
                        conn->cleanup = cis_cleanup;
 
+               conn->mtu = hdev->iso_mtu ? hdev->iso_mtu :
+                           hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
                break;
        case SCO_LINK:
                if (lmp_esco_capable(hdev))
@@ -961,9 +991,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;
        }
 
@@ -1006,7 +1039,7 @@ struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
 
        handle = hci_conn_hash_alloc_unset(hdev);
        if (unlikely(handle < 0))
-               return NULL;
+               return ERR_PTR(-ECONNREFUSED);
 
        return hci_conn_add(hdev, type, dst, role, handle);
 }
@@ -1312,8 +1345,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                bacpy(&conn->dst, dst);
        } else {
                conn = hci_conn_add_unset(hdev, LE_LINK, dst, role);
-               if (!conn)
-                       return ERR_PTR(-ENOMEM);
+               if (IS_ERR(conn))
+                       return conn;
                hci_conn_hold(conn);
                conn->pending_sec_level = sec_level;
        }
@@ -1489,8 +1522,8 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
                return ERR_PTR(-EADDRINUSE);
 
        conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
-       if (!conn)
-               return ERR_PTR(-ENOMEM);
+       if (IS_ERR(conn))
+               return conn;
 
        conn->state = BT_CONNECT;
 
@@ -1533,8 +1566,8 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
        BT_DBG("requesting refresh of dst_addr");
 
        conn = hci_conn_add_unset(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
-       if (!conn)
-               return ERR_PTR(-ENOMEM);
+       if (IS_ERR(conn))
+               return conn;
 
        if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0) {
                hci_conn_del(conn);
@@ -1581,8 +1614,8 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
        acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
        if (!acl) {
                acl = hci_conn_add_unset(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
-               if (!acl)
-                       return ERR_PTR(-ENOMEM);
+               if (IS_ERR(acl))
+                       return acl;
        }
 
        hci_conn_hold(acl);
@@ -1650,9 +1683,9 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
        sco = hci_conn_hash_lookup_ba(hdev, type, dst);
        if (!sco) {
                sco = hci_conn_add_unset(hdev, type, dst, HCI_ROLE_MASTER);
-               if (!sco) {
+               if (IS_ERR(sco)) {
                        hci_conn_drop(acl);
-                       return ERR_PTR(-ENOMEM);
+                       return sco;
                }
        }
 
@@ -1841,8 +1874,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
                                       qos->ucast.cis);
        if (!cis) {
                cis = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
-               if (!cis)
-                       return ERR_PTR(-ENOMEM);
+               if (IS_ERR(cis))
+                       return cis;
                cis->cleanup = cis_cleanup;
                cis->dst_type = dst_type;
                cis->iso_qos.ucast.cig = BT_ISO_QOS_CIG_UNSET;
@@ -1977,14 +2010,8 @@ static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
                              struct bt_iso_io_qos *qos, __u8 phy)
 {
        /* Only set MTU if PHY is enabled */
-       if (!qos->sdu && qos->phy) {
-               if (hdev->iso_mtu > 0)
-                       qos->sdu = hdev->iso_mtu;
-               else if (hdev->le_mtu > 0)
-                       qos->sdu = hdev->le_mtu;
-               else
-                       qos->sdu = hdev->acl_mtu;
-       }
+       if (!qos->sdu && qos->phy)
+               qos->sdu = conn->mtu;
 
        /* Use the same PHY as ACL if set to any */
        if (qos->phy == BT_ISO_PHY_ANY)
@@ -2065,8 +2092,8 @@ struct hci_conn *hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst,
                return ERR_PTR(-EBUSY);
 
        conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_SLAVE);
-       if (!conn)
-               return ERR_PTR(-ENOMEM);
+       if (IS_ERR(conn))
+               return conn;
 
        conn->iso_qos = *qos;
        conn->state = BT_LISTEN;