aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuiyi Zhang <Ruiyi.Zhang@Atheros.com>2011-12-13 01:22:50 +0800
committerAndy Green <andy.green@linaro.org>2012-01-09 10:52:12 +0800
commitf793c478ccb0437548e545bbc804e135f0cf15cd (patch)
treea3788830c0302ea11deea9858f06df28adf6f163
parent655a987e301613e41bd48cdcc71c976fa13116c8 (diff)
downloadimx53-f793c478ccb0437548e545bbc804e135f0cf15cd.tar.gz
Bluetooth: Allow unsegmented SDU retries on sock_queue_rcv_skb failure
In L2CAP_SDU_UNSEGMENTED case, if sock_queue_rcv_skb returns error, l2cap_ertm_reassembly_sdu should not return 0 so as to insert the skb into BUSY_QUEUE for later retries. Signed-off-by: Ruiyi Zhang <Ruiyi.Zhang@Atheros.com> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r--net/bluetooth/l2cap_core.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 883c0d221c0..dda890dabd4 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3289,6 +3289,95 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
}
}
+static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+{
+ struct sk_buff *_skb;
+ int err = -EINVAL;
+
+ /*
+ * TODO: We have to notify the userland if some data is lost with the
+ * Streaming Mode.
+ */
+
+ switch (control & L2CAP_CTRL_SAR) {
+ case L2CAP_SDU_UNSEGMENTED:
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
+ kfree_skb(chan->sdu);
+ break;
+ }
+
+ return chan->ops->recv(chan->data, skb);
+
+ case L2CAP_SDU_START:
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
+ kfree_skb(chan->sdu);
+ break;
+ }
+
+ chan->sdu_len = get_unaligned_le16(skb->data);
+ skb_pull(skb, 2);
+
+ if (chan->sdu_len > chan->imtu) {
+ err = -EMSGSIZE;
+ break;
+ }
+
+ chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
+ if (!chan->sdu) {
+ err = -ENOMEM;
+ break;
+ }
+
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+
+ set_bit(CONN_SAR_SDU, &chan->conn_state);
+ chan->partial_sdu_len = skb->len;
+ err = 0;
+ break;
+
+ case L2CAP_SDU_CONTINUE:
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
+ break;
+
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+
+ chan->partial_sdu_len += skb->len;
+ if (chan->partial_sdu_len > chan->sdu_len)
+ kfree_skb(chan->sdu);
+ else
+ err = 0;
+
+ break;
+
+ case L2CAP_SDU_END:
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
+ break;
+
+ memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
+
+ clear_bit(CONN_SAR_SDU, &chan->conn_state);
+ chan->partial_sdu_len += skb->len;
+
+ if (chan->partial_sdu_len > chan->imtu)
+ goto drop;
+
+ if (chan->partial_sdu_len == chan->sdu_len) {
+ _skb = skb_clone(chan->sdu, GFP_ATOMIC);
+ err = chan->ops->recv(chan->data, _skb);
+ if (err < 0)
+ kfree_skb(_skb);
+ }
+ err = 0;
+
+drop:
+ kfree_skb(chan->sdu);
+ break;
+ }
+
+ kfree_skb(skb);
+ return err;
+}
+
static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
{
struct sk_buff *skb;