aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/l2cap_core.c
diff options
context:
space:
mode:
authorMat Martineau <mathewm@codeaurora.org>2012-05-17 20:53:44 -0700
committerJohan Hedberg <johan.hedberg@intel.com>2012-06-05 06:34:04 +0300
commitbed68bde7ebdb591cc67921261307626c8f37936 (patch)
tree67a920542cc82703bf00fffcca6ab03e69f0dba6 /net/bluetooth/l2cap_core.c
parentBluetooth: Add implementation for retransmitting all unacked frames (diff)
downloadlinux-dev-bed68bde7ebdb591cc67921261307626c8f37936.tar.xz
linux-dev-bed68bde7ebdb591cc67921261307626c8f37936.zip
Bluetooth: Send SREJ frames when packets go missing
The ERTM specification lays out three scenarios for sending SREJ frames to request retransmission of specific frames. l2cap_send_srej requests all frames up to a given txseq that are not already queued for reassembly. l2cap_send_srej_tail only requests the most recent missing frame. l2cap_send_srej_list resends SREJ frames for data that was requested for resend but never received. Signed-off-by: Mat Martineau <mathewm@codeaurora.org> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Diffstat (limited to 'net/bluetooth/l2cap_core.c')
-rw-r--r--net/bluetooth/l2cap_core.c56
1 files changed, 53 insertions, 3 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 26963a5e3f58..5823697cf9de 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2239,17 +2239,67 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
{
- /* Placeholder */
+ struct l2cap_ctrl control;
+ u16 seq;
+
+ BT_DBG("chan %p, txseq %d", chan, txseq);
+
+ memset(&control, 0, sizeof(control));
+ control.sframe = 1;
+ control.super = L2CAP_SUPER_SREJ;
+
+ for (seq = chan->expected_tx_seq; seq != txseq;
+ seq = __next_seq(chan, seq)) {
+ if (!l2cap_ertm_seq_in_queue(&chan->srej_q, seq)) {
+ control.reqseq = seq;
+ l2cap_send_sframe(chan, &control);
+ l2cap_seq_list_append(&chan->srej_list, seq);
+ }
+ }
+
+ chan->expected_tx_seq = __next_seq(chan, txseq);
}
static void l2cap_send_srej_tail(struct l2cap_chan *chan)
{
- /* Placeholder */
+ struct l2cap_ctrl control;
+
+ BT_DBG("chan %p", chan);
+
+ if (chan->srej_list.tail == L2CAP_SEQ_LIST_CLEAR)
+ return;
+
+ memset(&control, 0, sizeof(control));
+ control.sframe = 1;
+ control.super = L2CAP_SUPER_SREJ;
+ control.reqseq = chan->srej_list.tail;
+ l2cap_send_sframe(chan, &control);
}
static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq)
{
- /* Placeholder */
+ struct l2cap_ctrl control;
+ u16 initial_head;
+ u16 seq;
+
+ BT_DBG("chan %p, txseq %d", chan, txseq);
+
+ memset(&control, 0, sizeof(control));
+ control.sframe = 1;
+ control.super = L2CAP_SUPER_SREJ;
+
+ /* Capture initial list head to allow only one pass through the list. */
+ initial_head = chan->srej_list.head;
+
+ do {
+ seq = l2cap_seq_list_pop(&chan->srej_list);
+ if (seq == txseq || seq == L2CAP_SEQ_LIST_CLEAR)
+ break;
+
+ control.reqseq = seq;
+ l2cap_send_sframe(chan, &control);
+ l2cap_seq_list_append(&chan->srej_list, seq);
+ } while (chan->srej_list.head != initial_head);
}
static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)