printk: Remove no longer used LOG_PREFIX.
[linux-2.6-microblaze.git] / net / bluetooth / l2cap_core.c
index d17a473..2146e0f 100644 (file)
@@ -51,9 +51,6 @@ static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
 
-static u16 le_max_credits = L2CAP_LE_MAX_CREDITS;
-static u16 le_default_mps = L2CAP_LE_DEFAULT_MPS;
-
 static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
                                       u8 code, u8 ident, u16 dlen, void *data);
 static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
@@ -519,8 +516,10 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
        chan->sdu_last_frag = NULL;
        chan->sdu_len = 0;
        chan->tx_credits = 0;
-       chan->rx_credits = le_max_credits;
-       chan->mps = min_t(u16, chan->imtu, le_default_mps);
+       /* Derive MPS from connection MTU to stop HCI fragmentation */
+       chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE);
+       /* Give enough credits for a full packet */
+       chan->rx_credits = (chan->imtu / chan->mps) + 1;
 
        skb_queue_head_init(&chan->tx_q);
 }
@@ -681,9 +680,9 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
        u16 result;
 
        if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
-               result = L2CAP_CR_AUTHORIZATION;
+               result = L2CAP_CR_LE_AUTHORIZATION;
        else
-               result = L2CAP_CR_BAD_PSM;
+               result = L2CAP_CR_LE_BAD_PSM;
 
        l2cap_state_change(chan, BT_DISCONN);
 
@@ -1282,6 +1281,8 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
        if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
                return;
 
+       l2cap_le_flowctl_init(chan);
+
        req.psm     = chan->psm;
        req.scid    = cpu_to_le16(chan->scid);
        req.mtu     = cpu_to_le16(chan->imtu);
@@ -3669,7 +3670,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
        rsp.mtu     = cpu_to_le16(chan->imtu);
        rsp.mps     = cpu_to_le16(chan->mps);
        rsp.credits = cpu_to_le16(chan->rx_credits);
-       rsp.result  = cpu_to_le16(L2CAP_CR_SUCCESS);
+       rsp.result  = cpu_to_le16(L2CAP_CR_LE_SUCCESS);
 
        l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
                       &rsp);
@@ -3815,9 +3816,17 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        result = L2CAP_CR_NO_MEM;
 
+       /* Check for valid dynamic CID range (as per Erratum 3253) */
+       if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_DYN_END) {
+               result = L2CAP_CR_INVALID_SCID;
+               goto response;
+       }
+
        /* Check if we already have channel with that dcid */
-       if (__l2cap_get_chan_by_dcid(conn, scid))
+       if (__l2cap_get_chan_by_dcid(conn, scid)) {
+               result = L2CAP_CR_SCID_IN_USE;
                goto response;
+       }
 
        chan = pchan->ops->new_connection(pchan);
        if (!chan)
@@ -5279,7 +5288,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
        credits = __le16_to_cpu(rsp->credits);
        result  = __le16_to_cpu(rsp->result);
 
-       if (result == L2CAP_CR_SUCCESS && (mtu < 23 || mps < 23 ||
+       if (result == L2CAP_CR_LE_SUCCESS && (mtu < 23 || mps < 23 ||
                                           dcid < L2CAP_CID_DYN_START ||
                                           dcid > L2CAP_CID_LE_DYN_END))
                return -EPROTO;
@@ -5300,7 +5309,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
        l2cap_chan_lock(chan);
 
        switch (result) {
-       case L2CAP_CR_SUCCESS:
+       case L2CAP_CR_LE_SUCCESS:
                if (__l2cap_get_chan_by_dcid(conn, dcid)) {
                        err = -EBADSLT;
                        break;
@@ -5314,8 +5323,8 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
                l2cap_chan_ready(chan);
                break;
 
-       case L2CAP_CR_AUTHENTICATION:
-       case L2CAP_CR_ENCRYPTION:
+       case L2CAP_CR_LE_AUTHENTICATION:
+       case L2CAP_CR_LE_ENCRYPTION:
                /* If we already have MITM protection we can't do
                 * anything.
                 */
@@ -5458,7 +5467,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
        pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
                                         &conn->hcon->dst, LE_LINK);
        if (!pchan) {
-               result = L2CAP_CR_BAD_PSM;
+               result = L2CAP_CR_LE_BAD_PSM;
                chan = NULL;
                goto response;
        }
@@ -5468,33 +5477,31 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
 
        if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
                                     SMP_ALLOW_STK)) {
-               result = L2CAP_CR_AUTHENTICATION;
+               result = L2CAP_CR_LE_AUTHENTICATION;
                chan = NULL;
                goto response_unlock;
        }
 
        /* Check for valid dynamic CID range */
        if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
-               result = L2CAP_CR_INVALID_SCID;
+               result = L2CAP_CR_LE_INVALID_SCID;
                chan = NULL;
                goto response_unlock;
        }
 
        /* Check if we already have channel with that dcid */
        if (__l2cap_get_chan_by_dcid(conn, scid)) {
-               result = L2CAP_CR_SCID_IN_USE;
+               result = L2CAP_CR_LE_SCID_IN_USE;
                chan = NULL;
                goto response_unlock;
        }
 
        chan = pchan->ops->new_connection(pchan);
        if (!chan) {
-               result = L2CAP_CR_NO_MEM;
+               result = L2CAP_CR_LE_NO_MEM;
                goto response_unlock;
        }
 
-       l2cap_le_flowctl_init(chan);
-
        bacpy(&chan->src, &conn->hcon->src);
        bacpy(&chan->dst, &conn->hcon->dst);
        chan->src_type = bdaddr_src_type(conn->hcon);
@@ -5506,6 +5513,9 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
        chan->tx_credits = __le16_to_cpu(req->credits);
 
        __l2cap_chan_add(conn, chan);
+
+       l2cap_le_flowctl_init(chan);
+
        dcid = chan->scid;
        credits = chan->rx_credits;
 
@@ -5524,7 +5534,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
                chan->ops->defer(chan);
        } else {
                l2cap_chan_ready(chan);
-               result = L2CAP_CR_SUCCESS;
+               result = L2CAP_CR_LE_SUCCESS;
        }
 
 response_unlock:
@@ -6699,13 +6709,10 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
        struct l2cap_le_credits pkt;
        u16 return_credits;
 
-       /* We return more credits to the sender only after the amount of
-        * credits falls below half of the initial amount.
-        */
-       if (chan->rx_credits >= (le_max_credits + 1) / 2)
-               return;
+       return_credits = ((chan->imtu / chan->mps) + 1) - chan->rx_credits;
 
-       return_credits = le_max_credits - chan->rx_credits;
+       if (!return_credits)
+               return;
 
        BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
 
@@ -6719,6 +6726,21 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
        l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
 }
 
+static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+       int err;
+
+       BT_DBG("SDU reassemble complete: chan %p skb->len %u", chan, skb->len);
+
+       /* Wait recv to confirm reception before updating the credits */
+       err = chan->ops->recv(chan, skb);
+
+       /* Update credits whenever an SDU is received */
+       l2cap_chan_le_send_credits(chan);
+
+       return err;
+}
+
 static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        int err;
@@ -6737,7 +6759,11 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
        chan->rx_credits--;
        BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
 
-       l2cap_chan_le_send_credits(chan);
+       /* Update if remote had run out of credits, this should only happens
+        * if the remote is not using the entire MPS.
+        */
+       if (!chan->rx_credits)
+               l2cap_chan_le_send_credits(chan);
 
        err = 0;
 
@@ -6763,12 +6789,22 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
                }
 
                if (skb->len == sdu_len)
-                       return chan->ops->recv(chan, skb);
+                       return l2cap_le_recv(chan, skb);
 
                chan->sdu = skb;
                chan->sdu_len = sdu_len;
                chan->sdu_last_frag = skb;
 
+               /* Detect if remote is not able to use the selected MPS */
+               if (skb->len + L2CAP_SDULEN_SIZE < chan->mps) {
+                       u16 mps_len = skb->len + L2CAP_SDULEN_SIZE;
+
+                       /* Adjust the number of credits */
+                       BT_DBG("chan->mps %u -> %u", chan->mps, mps_len);
+                       chan->mps = mps_len;
+                       l2cap_chan_le_send_credits(chan);
+               }
+
                return 0;
        }
 
@@ -6785,7 +6821,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
        skb = NULL;
 
        if (chan->sdu->len == chan->sdu_len) {
-               err = chan->ops->recv(chan, chan->sdu);
+               err = l2cap_le_recv(chan, chan->sdu);
                if (!err) {
                        chan->sdu = NULL;
                        chan->sdu_last_frag = NULL;
@@ -7102,7 +7138,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        case L2CAP_MODE_BASIC:
                break;
        case L2CAP_MODE_LE_FLOWCTL:
-               l2cap_le_flowctl_init(chan);
                break;
        case L2CAP_MODE_ERTM:
        case L2CAP_MODE_STREAMING:
@@ -7645,11 +7680,6 @@ int __init l2cap_init(void)
        l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
                                            NULL, &l2cap_debugfs_fops);
 
-       debugfs_create_u16("l2cap_le_max_credits", 0644, bt_debugfs,
-                          &le_max_credits);
-       debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs,
-                          &le_default_mps);
-
        return 0;
 }