|
@@ -51,8 +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 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,
|
|
@@ -518,9 +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;
|
|
|
/* 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);
|
|
|
}
|
|
@@ -6702,13 +6701,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);
|
|
|
|
|
@@ -6722,6 +6718,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;
|
|
@@ -6740,7 +6751,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;
|
|
|
|
|
@@ -6766,7 +6781,7 @@ 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;
|
|
@@ -6788,7 +6803,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;
|
|
@@ -7647,9 +7662,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);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|