diff options
author | Mat Martineau <mathewm@codeaurora.org> | 2012-05-17 23:53:46 -0400 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2012-06-04 23:34:04 -0400 |
commit | f80842a83ec224e70ebbd11a20832e71e5911b45 (patch) | |
tree | 98bf84971775e5deb4aaf647dd1d553aa201d0fc /net/bluetooth/l2cap_core.c | |
parent | 63838725c6478102894cfb88feb2a9b1c331855d (diff) |
Bluetooth: Handle SREJ requests to resend unacked frames
When a remote device sends an SREJ, retransmit the frame with the
corresponding sequence number (subject to special cases with poll and
final flags). An SREJ is also an implicit indication the the remote
device is not in a busy state.
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.c | 70 |
1 files changed, 69 insertions, 1 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fd324d4cb217..36842a29bb47 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -1836,6 +1836,15 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) | |||
1836 | } | 1836 | } |
1837 | } | 1837 | } |
1838 | 1838 | ||
1839 | static void l2cap_retransmit(struct l2cap_chan *chan, | ||
1840 | struct l2cap_ctrl *control) | ||
1841 | { | ||
1842 | BT_DBG("chan %p, control %p", chan, control); | ||
1843 | |||
1844 | l2cap_seq_list_append(&chan->retrans_list, control->reqseq); | ||
1845 | l2cap_ertm_resend(chan); | ||
1846 | } | ||
1847 | |||
1839 | static void l2cap_retransmit_all(struct l2cap_chan *chan, | 1848 | static void l2cap_retransmit_all(struct l2cap_chan *chan, |
1840 | struct l2cap_ctrl *control) | 1849 | struct l2cap_ctrl *control) |
1841 | { | 1850 | { |
@@ -2532,6 +2541,13 @@ static void l2cap_pass_to_tx(struct l2cap_chan *chan, | |||
2532 | l2cap_tx(chan, control, 0, L2CAP_EV_RECV_REQSEQ_AND_FBIT); | 2541 | l2cap_tx(chan, control, 0, L2CAP_EV_RECV_REQSEQ_AND_FBIT); |
2533 | } | 2542 | } |
2534 | 2543 | ||
2544 | static void l2cap_pass_to_tx_fbit(struct l2cap_chan *chan, | ||
2545 | struct l2cap_ctrl *control) | ||
2546 | { | ||
2547 | BT_DBG("chan %p, control %p", chan, control); | ||
2548 | l2cap_tx(chan, control, 0, L2CAP_EV_RECV_FBIT); | ||
2549 | } | ||
2550 | |||
2535 | /* Copy frame to all raw sockets on that connection */ | 2551 | /* Copy frame to all raw sockets on that connection */ |
2536 | static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) | 2552 | static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) |
2537 | { | 2553 | { |
@@ -4539,7 +4555,59 @@ static int l2cap_rx_queued_iframes(struct l2cap_chan *chan) | |||
4539 | static void l2cap_handle_srej(struct l2cap_chan *chan, | 4555 | static void l2cap_handle_srej(struct l2cap_chan *chan, |
4540 | struct l2cap_ctrl *control) | 4556 | struct l2cap_ctrl *control) |
4541 | { | 4557 | { |
4542 | /* Placeholder */ | 4558 | struct sk_buff *skb; |
4559 | |||
4560 | BT_DBG("chan %p, control %p", chan, control); | ||
4561 | |||
4562 | if (control->reqseq == chan->next_tx_seq) { | ||
4563 | BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); | ||
4564 | l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); | ||
4565 | return; | ||
4566 | } | ||
4567 | |||
4568 | skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq); | ||
4569 | |||
4570 | if (skb == NULL) { | ||
4571 | BT_DBG("Seq %d not available for retransmission", | ||
4572 | control->reqseq); | ||
4573 | return; | ||
4574 | } | ||
4575 | |||
4576 | if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) { | ||
4577 | BT_DBG("Retry limit exceeded (%d)", chan->max_tx); | ||
4578 | l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); | ||
4579 | return; | ||
4580 | } | ||
4581 | |||
4582 | clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); | ||
4583 | |||
4584 | if (control->poll) { | ||
4585 | l2cap_pass_to_tx(chan, control); | ||
4586 | |||
4587 | set_bit(CONN_SEND_FBIT, &chan->conn_state); | ||
4588 | l2cap_retransmit(chan, control); | ||
4589 | l2cap_ertm_send(chan); | ||
4590 | |||
4591 | if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) { | ||
4592 | set_bit(CONN_SREJ_ACT, &chan->conn_state); | ||
4593 | chan->srej_save_reqseq = control->reqseq; | ||
4594 | } | ||
4595 | } else { | ||
4596 | l2cap_pass_to_tx_fbit(chan, control); | ||
4597 | |||
4598 | if (control->final) { | ||
4599 | if (chan->srej_save_reqseq != control->reqseq || | ||
4600 | !test_and_clear_bit(CONN_SREJ_ACT, | ||
4601 | &chan->conn_state)) | ||
4602 | l2cap_retransmit(chan, control); | ||
4603 | } else { | ||
4604 | l2cap_retransmit(chan, control); | ||
4605 | if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) { | ||
4606 | set_bit(CONN_SREJ_ACT, &chan->conn_state); | ||
4607 | chan->srej_save_reqseq = control->reqseq; | ||
4608 | } | ||
4609 | } | ||
4610 | } | ||
4543 | } | 4611 | } |
4544 | 4612 | ||
4545 | static void l2cap_handle_rej(struct l2cap_chan *chan, | 4613 | static void l2cap_handle_rej(struct l2cap_chan *chan, |