Merge tag 'xfs-for-linus-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / net / bluetooth / l2cap_core.c
index 4a90438..b6f9777 100644 (file)
@@ -546,7 +546,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 
        l2cap_chan_hold(chan);
 
-       hci_conn_hold(conn->hcon);
+       /* Only keep a reference for fixed channels if they requested it */
+       if (chan->chan_type != L2CAP_CHAN_FIXED ||
+           test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
+               hci_conn_hold(conn->hcon);
 
        list_add(&chan->list, &conn->chan_l);
 }
@@ -577,7 +580,12 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 
                chan->conn = NULL;
 
-               if (chan->scid != L2CAP_CID_A2MP)
+               /* Reference was only held for non-fixed channels or
+                * fixed channels that explicitly requested it using the
+                * FLAG_HOLD_HCI_CONN flag.
+                */
+               if (chan->chan_type != L2CAP_CHAN_FIXED ||
+                   test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
                        hci_conn_drop(conn->hcon);
 
                if (mgr && mgr->bredr_chan == chan)
@@ -623,9 +631,11 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 }
 EXPORT_SYMBOL_GPL(l2cap_chan_del);
 
-void l2cap_conn_update_id_addr(struct hci_conn *hcon)
+static void l2cap_conn_update_id_addr(struct work_struct *work)
 {
-       struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+                                              id_addr_update_work);
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan;
 
        mutex_lock(&conn->chan_lock);
@@ -1273,6 +1283,24 @@ static void l2cap_start_connection(struct l2cap_chan *chan)
        }
 }
 
+static void l2cap_request_info(struct l2cap_conn *conn)
+{
+       struct l2cap_info_req req;
+
+       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+               return;
+
+       req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
+
+       conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
+       conn->info_ident = l2cap_get_ident(conn);
+
+       schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
+
+       l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
+                      sizeof(req), &req);
+}
+
 static void l2cap_do_start(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
@@ -1282,26 +1310,17 @@ static void l2cap_do_start(struct l2cap_chan *chan)
                return;
        }
 
-       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
-               if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
-                       return;
-
-               if (l2cap_chan_check_security(chan, true) &&
-                   __l2cap_no_conn_pending(chan)) {
-                       l2cap_start_connection(chan);
-               }
-       } else {
-               struct l2cap_info_req req;
-               req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
-
-               conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
-               conn->info_ident = l2cap_get_ident(conn);
+       if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
+               l2cap_request_info(conn);
+               return;
+       }
 
-               schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
+       if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+               return;
 
-               l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
-                              sizeof(req), &req);
-       }
+       if (l2cap_chan_check_security(chan, true) &&
+           __l2cap_no_conn_pending(chan))
+               l2cap_start_connection(chan);
 }
 
 static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
@@ -1360,6 +1379,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                l2cap_chan_lock(chan);
 
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+                       l2cap_chan_ready(chan);
                        l2cap_chan_unlock(chan);
                        continue;
                }
@@ -1464,6 +1484,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
        BT_DBG("conn %p", conn);
 
+       if (hcon->type == ACL_LINK)
+               l2cap_request_info(conn);
+
        mutex_lock(&conn->chan_lock);
 
        list_for_each_entry(chan, &conn->chan_l, list) {
@@ -1478,8 +1501,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                if (hcon->type == LE_LINK) {
                        l2cap_le_start(chan);
                } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       l2cap_chan_ready(chan);
-
+                       if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
+                               l2cap_chan_ready(chan);
                } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
                }
@@ -1627,11 +1650,14 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
        if (work_pending(&conn->pending_rx_work))
                cancel_work_sync(&conn->pending_rx_work);
 
-       if (work_pending(&conn->disconn_work))
-               cancel_work_sync(&conn->disconn_work);
+       if (work_pending(&conn->id_addr_update_work))
+               cancel_work_sync(&conn->id_addr_update_work);
 
        l2cap_unregister_all_users(conn);
 
+       /* Force the connection to be immediately dropped */
+       hcon->disc_timeout = 0;
+
        mutex_lock(&conn->chan_lock);
 
        /* Kill channels */
@@ -1659,26 +1685,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
        l2cap_conn_put(conn);
 }
 
-static void disconn_work(struct work_struct *work)
-{
-       struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
-                                              disconn_work);
-
-       BT_DBG("conn %p", conn);
-
-       l2cap_conn_del(conn->hcon, conn->disconn_err);
-}
-
-void l2cap_conn_shutdown(struct l2cap_conn *conn, int err)
-{
-       struct hci_dev *hdev = conn->hcon->hdev;
-
-       BT_DBG("conn %p err %d", conn, err);
-
-       conn->disconn_err = err;
-       queue_work(hdev->workqueue, &conn->disconn_work);
-}
-
 static void l2cap_conn_free(struct kref *ref)
 {
        struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1687,9 +1693,10 @@ static void l2cap_conn_free(struct kref *ref)
        kfree(conn);
 }
 
-void l2cap_conn_get(struct l2cap_conn *conn)
+struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn)
 {
        kref_get(&conn->ref);
+       return conn;
 }
 EXPORT_SYMBOL(l2cap_conn_get);
 
@@ -2358,12 +2365,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
 
        BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
 
-       pdu_len = chan->conn->mtu - L2CAP_HDR_SIZE;
-
-       pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
-
        sdu_len = len;
-       pdu_len -= L2CAP_SDULEN_SIZE;
+       pdu_len = chan->remote_mps - L2CAP_SDULEN_SIZE;
 
        while (len > 0) {
                if (len <= pdu_len)
@@ -5428,6 +5431,11 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
 
        if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                l2cap_state_change(chan, BT_CONNECT2);
+               /* The following result value is actually not defined
+                * for LE CoC but we use it to let the function know
+                * that it should bail out after doing its cleanup
+                * instead of sending a response.
+                */
                result = L2CAP_CR_PEND;
                chan->ops->defer(chan);
        } else {
@@ -6904,8 +6912,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 
        kref_init(&conn->ref);
        hcon->l2cap_data = conn;
-       conn->hcon = hcon;
-       hci_conn_get(conn->hcon);
+       conn->hcon = hci_conn_get(hcon);
        conn->hchan = hchan;
 
        BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
@@ -6936,10 +6943,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 
        INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
 
-       INIT_WORK(&conn->disconn_work, disconn_work);
-
        skb_queue_head_init(&conn->pending_rx);
        INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+       INIT_WORK(&conn->id_addr_update_work, l2cap_conn_update_id_addr);
 
        conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
 
@@ -6974,8 +6980,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        hci_dev_lock(hdev);
 
-       l2cap_chan_lock(chan);
-
        if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
            chan->chan_type != L2CAP_CHAN_RAW) {
                err = -EINVAL;
@@ -7072,19 +7076,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                goto done;
        }
 
+       mutex_lock(&conn->chan_lock);
+       l2cap_chan_lock(chan);
+
        if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
                hci_conn_drop(hcon);
                err = -EBUSY;
-               goto done;
+               goto chan_unlock;
        }
 
        /* Update source addr of the socket */
        bacpy(&chan->src, &hcon->src);
        chan->src_type = bdaddr_type(hcon, hcon->src_type);
 
-       l2cap_chan_unlock(chan);
-       l2cap_chan_add(conn, chan);
-       l2cap_chan_lock(chan);
+       __l2cap_chan_add(conn, chan);
 
        /* l2cap_chan_add takes its own ref so we can drop this one */
        hci_conn_drop(hcon);
@@ -7110,8 +7115,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
        err = 0;
 
-done:
+chan_unlock:
        l2cap_chan_unlock(chan);
+       mutex_unlock(&conn->chan_lock);
+done:
        hci_dev_unlock(hdev);
        hci_dev_put(hdev);
        return err;