All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/25] ERTM state machine changes, part 3
@ 2012-05-18  3:53 Mat Martineau
  2012-05-18  3:53 ` [PATCH 01/25] Bluetooth: Change default state of ERTM disable flag Mat Martineau
                   ` (24 more replies)
  0 siblings, 25 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This completes the switch to the new ERTM state machine.  The patch set 
needs to be merged all at once, since ERTM will only work with all of 
the changes applied.

There was no way to replace the state machine incrementally, since this 
is switching ERTM to a whole different design everything needs to be 
swapped out at once.  I checked with Marcel on this approach in advance.

We're running PTS on this code this week, I will let the list know about 
the test results.

Mat Martineau (25):
  Bluetooth: Change default state of ERTM disable flag
  Bluetooth: Add a new L2CAP ERTM transmit state machine.
  Bluetooth: Refactor l2cap_streaming_send
  Bluetooth: Refactor l2cap_ertm_send
  Bluetooth: Refactor l2cap_send_sframe
  Bluetooth: Consolidate common receive code for ERTM and streaming
    mode
  Bluetooth: Add streaming mode receive and incoming packet classifier
  Bluetooth: Remove receive code that has been superceded
  Bluetooth: Refactor l2cap_send_ack
  Bluetooth: Use the transmit state machine for busy state changes
  Bluetooth: Update l2cap_send_i_or_rr_or_rnr to fit the spec better
  Bluetooth: Add the ERTM receive state machine
  Bluetooth: Add implementation for retransmitting all unacked frames
  Bluetooth: Send SREJ frames when packets go missing
  Bluetooth: Reassemble all available data when retransmissions
    succeed.
  Bluetooth: Handle SREJ requests to resend unacked frames
  Bluetooth: Handle incoming REJ frames
  Bluetooth: Use new header structures in l2cap_send_rr_or_rnr
  Bluetooth: Check rules when setting retransmit or monitor timers
  Bluetooth: Use the ERTM transmit state machine from timeout handlers
  Bluetooth: Simplify the ERTM ack timeout
  Bluetooth: Remove unneccesary inline
  Bluetooth: Set txwin values for streaming mode
  Bluetooth: Remove unused ERTM control field macros
  Bluetooth: Enable ERTM by default

 include/net/bluetooth/l2cap.h |  180 ----
 net/bluetooth/l2cap_core.c    | 1977 ++++++++++++++++++++++++++---------------
 2 files changed, 1262 insertions(+), 895 deletions(-)

-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 01/25] Bluetooth: Change default state of ERTM disable flag
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine Mat Martineau
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This is to allow for ERTM state machine replacement in the patches
that follow.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index aeb4e6e..6557367 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -57,7 +57,7 @@
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/smp.h>
 
-bool disable_ertm;
+bool disable_ertm = 1;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
 static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine.
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
  2012-05-18  3:53 ` [PATCH 01/25] Bluetooth: Change default state of ERTM disable flag Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18 19:54   ` Gustavo Padovan
  2012-05-18  3:53 ` [PATCH 03/25] Bluetooth: Refactor l2cap_streaming_send Mat Martineau
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This implements a top-level transmit state machine with handlers for
the two ERTM states defined in the specification: XMIT and WAIT_F.

The state machine accepts an event and, optionally, a list of skbs to
transmit.  In addition to data transmission, the local busy state can
be modified, acks are processed, and monitor and retransmit timeouts
are handled.  This mirrors the structure of the state tables in the
spec.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |  254 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 246 insertions(+), 8 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 6557367..a131403 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -73,6 +73,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
 static void l2cap_send_disconn_req(struct l2cap_conn *conn,
 				   struct l2cap_chan *chan, int err);
 
+static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
+		    struct sk_buff_head *skbs, u8 event);
+
 /* ---- L2CAP channels ---- */
 
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
@@ -224,6 +227,19 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
 	release_sock(sk);
 }
 
+static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
+					       u16 seq)
+{
+	struct sk_buff *skb;
+
+	skb_queue_walk(head, skb) {
+		if (bt_cb(skb)->control.txseq == seq)
+			return skb;
+	}
+
+	return NULL;
+}
+
 /* ---- L2CAP sequence number lists ---- */
 
 /* For ERTM, ordered lists of sequence numbers must be tracked for
@@ -2117,16 +2133,15 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 		if (err)
 			break;
 
-		if (chan->mode == L2CAP_MODE_ERTM && chan->tx_send_head == NULL)
-			chan->tx_send_head = seg_queue.next;
-		skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
-
-		if (chan->mode == L2CAP_MODE_ERTM)
-			err = l2cap_ertm_send(chan);
-		else
+		if (chan->mode == L2CAP_MODE_ERTM) {
+			err = l2cap_tx(chan, 0, &seg_queue,
+				       L2CAP_EV_DATA_REQUEST);
+		} else {
+			skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
 			l2cap_streaming_send(chan);
+		}
 
-		if (err >= 0)
+		if (!err)
 			err = len;
 
 		/* If the skbs were not queued for sending, they'll still be in
@@ -2143,6 +2158,229 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 	return err;
 }
 
+static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
+{
+	struct sk_buff *acked_skb;
+	u16 ackseq;
+
+	BT_DBG("chan %p, reqseq %d", chan, reqseq);
+
+	if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq)
+		return;
+
+	BT_DBG("expected_ack_seq %d, unacked_frames %d",
+	       chan->expected_ack_seq, chan->unacked_frames);
+
+	for (ackseq = chan->expected_ack_seq; ackseq != reqseq;
+	     ackseq = __next_seq(chan, ackseq)) {
+
+		acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq);
+		if (acked_skb) {
+			skb_unlink(acked_skb, &chan->tx_q);
+			kfree_skb(acked_skb);
+			chan->unacked_frames--;
+		}
+	}
+
+	chan->expected_ack_seq = reqseq;
+
+	if (chan->unacked_frames == 0)
+		__clear_retrans_timer(chan);
+
+	BT_DBG("unacked_frames %d", (int) chan->unacked_frames);
+}
+
+static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan)
+{
+	BT_DBG("chan %p", chan);
+
+	chan->expected_tx_seq = chan->buffer_seq;
+	l2cap_seq_list_clear(&chan->srej_list);
+	skb_queue_purge(&chan->srej_q);
+	chan->rx_state = L2CAP_RX_STATE_RECV;
+}
+
+static int l2cap_tx_state_xmit(struct l2cap_chan *chan,
+			       struct l2cap_ctrl *control,
+			       struct sk_buff_head *skbs, u8 event)
+{
+	int err = 0;
+
+	BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
+	       event);
+
+	switch (event) {
+	case L2CAP_EV_DATA_REQUEST:
+		if (chan->tx_send_head == NULL)
+			chan->tx_send_head = skb_peek(skbs);
+
+		skb_queue_splice_tail_init(skbs, &chan->tx_q);
+		l2cap_ertm_send(chan);
+		break;
+	case L2CAP_EV_LOCAL_BUSY_DETECTED:
+		BT_DBG("Enter LOCAL_BUSY");
+		set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+
+		if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
+			/* The SREJ_SENT state must be aborted if we are to
+			 * enter the LOCAL_BUSY state.
+			 */
+			l2cap_abort_rx_srej_sent(chan);
+		}
+
+		l2cap_send_ack(chan);
+
+		break;
+	case L2CAP_EV_LOCAL_BUSY_CLEAR:
+		BT_DBG("Exit LOCAL_BUSY");
+		clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+
+		if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
+			struct l2cap_ctrl local_control;
+
+			memset(&local_control, 0, sizeof(local_control));
+			local_control.sframe = 1;
+			local_control.super = L2CAP_SUPER_RR;
+			local_control.poll = 1;
+			local_control.reqseq = chan->buffer_seq;
+			l2cap_send_sframe(chan, 0);
+
+			chan->retry_count = 1;
+			__set_monitor_timer(chan);
+			chan->tx_state = L2CAP_TX_STATE_WAIT_F;
+		}
+		break;
+	case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
+		l2cap_process_reqseq(chan, control->reqseq);
+		break;
+	case L2CAP_EV_EXPLICIT_POLL:
+		l2cap_send_rr_or_rnr(chan, 1);
+		chan->retry_count = 1;
+		__set_monitor_timer(chan);
+		__clear_ack_timer(chan);
+		chan->tx_state = L2CAP_TX_STATE_WAIT_F;
+		break;
+	case L2CAP_EV_RETRANS_TO:
+		l2cap_send_rr_or_rnr(chan, 1);
+		chan->retry_count = 1;
+		__set_monitor_timer(chan);
+		chan->tx_state = L2CAP_TX_STATE_WAIT_F;
+		break;
+	case L2CAP_EV_RECV_FBIT:
+		/* Nothing to process */
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+static int l2cap_tx_state_wait_f(struct l2cap_chan *chan,
+				 struct l2cap_ctrl *control,
+				 struct sk_buff_head *skbs, u8 event)
+{
+	int err = 0;
+
+	BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
+	       event);
+
+	switch (event) {
+	case L2CAP_EV_DATA_REQUEST:
+		if (chan->tx_send_head == NULL)
+			chan->tx_send_head = skb_peek(skbs);
+		/* Queue data, but don't send. */
+		skb_queue_splice_tail_init(skbs, &chan->tx_q);
+		break;
+	case L2CAP_EV_LOCAL_BUSY_DETECTED:
+		BT_DBG("Enter LOCAL_BUSY");
+		set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+
+		if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
+			/* The SREJ_SENT state must be aborted if we are to
+			 * enter the LOCAL_BUSY state.
+			 */
+			l2cap_abort_rx_srej_sent(chan);
+		}
+
+		l2cap_send_ack(chan);
+
+		break;
+	case L2CAP_EV_LOCAL_BUSY_CLEAR:
+		BT_DBG("Exit LOCAL_BUSY");
+		clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+
+		if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
+			struct l2cap_ctrl local_control;
+			memset(&local_control, 0, sizeof(local_control));
+			local_control.sframe = 1;
+			local_control.super = L2CAP_SUPER_RR;
+			local_control.poll = 1;
+			local_control.reqseq = chan->buffer_seq;
+			l2cap_send_sframe(chan, 0);
+
+			chan->retry_count = 1;
+			__set_monitor_timer(chan);
+			chan->tx_state = L2CAP_TX_STATE_WAIT_F;
+		}
+		break;
+	case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
+		l2cap_process_reqseq(chan, control->reqseq);
+
+		/* Fall through */
+
+	case L2CAP_EV_RECV_FBIT:
+		if (control && control->final) {
+			__clear_monitor_timer(chan);
+			if (chan->unacked_frames > 0)
+				__set_retrans_timer(chan);
+			chan->retry_count = 0;
+			chan->tx_state = L2CAP_TX_STATE_XMIT;
+			BT_DBG("recv fbit tx_state 0x2.2%x", chan->tx_state);
+		}
+		break;
+	case L2CAP_EV_EXPLICIT_POLL:
+		/* Ignore */
+		break;
+	case L2CAP_EV_MONITOR_TO:
+		if (chan->max_tx == 0 || chan->retry_count < chan->max_tx) {
+			l2cap_send_rr_or_rnr(chan, 1);
+			__set_monitor_timer(chan);
+			chan->retry_count++;
+		} else {
+			l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
+		    struct sk_buff_head *skbs, u8 event)
+{
+	int err = 0;
+
+	BT_DBG("chan %p, control %p, skbs %p, event %d, state %d",
+	       chan, control, skbs, event, chan->tx_state);
+
+	switch (chan->tx_state) {
+	case L2CAP_TX_STATE_XMIT:
+		err = l2cap_tx_state_xmit(chan, control, skbs, event);
+		break;
+	case L2CAP_TX_STATE_WAIT_F:
+		err = l2cap_tx_state_wait_f(chan, control, skbs, event);
+		break;
+	default:
+		/* Ignore event */
+		break;
+	}
+
+	return err;
+}
+
 /* Copy frame to all raw sockets on that connection */
 static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
 {
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 03/25] Bluetooth: Refactor l2cap_streaming_send
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
  2012-05-18  3:53 ` [PATCH 01/25] Bluetooth: Change default state of ERTM disable flag Mat Martineau
  2012-05-18  3:53 ` [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 04/25] Bluetooth: Refactor l2cap_ertm_send Mat Martineau
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This new implementation uses struct l2cap_ctrl to compose the
streaming mode headers.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   48 ++++++++++++++++++++++++++++----------------
 1 file changed, 31 insertions(+), 17 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a131403..93c7e67 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1647,29 +1647,45 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
 		__clear_retrans_timer(chan);
 }
 
-static void l2cap_streaming_send(struct l2cap_chan *chan)
+static int l2cap_streaming_send(struct l2cap_chan *chan,
+				struct sk_buff_head *skbs)
 {
 	struct sk_buff *skb;
-	u32 control;
-	u16 fcs;
+	struct l2cap_ctrl *control;
 
-	while ((skb = skb_dequeue(&chan->tx_q))) {
-		control = __get_control(chan, skb->data + L2CAP_HDR_SIZE);
-		control |= __set_txseq(chan, chan->next_tx_seq);
-		control |= __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
-		__put_control(chan, control, skb->data + L2CAP_HDR_SIZE);
+	BT_DBG("chan %p, skbs %p", chan, skbs);
+
+	if (chan->state != BT_CONNECTED)
+		return -ENOTCONN;
+
+	skb_queue_splice_tail_init(skbs, &chan->tx_q);
+
+	while (!skb_queue_empty(&chan->tx_q)) {
+
+		skb = skb_dequeue(&chan->tx_q);
+
+		bt_cb(skb)->control.retries = 1;
+		control = &bt_cb(skb)->control;
+
+		control->reqseq = 0;
+		control->txseq = chan->next_tx_seq;
+
+		__pack_control(chan, control, skb);
 
 		if (chan->fcs == L2CAP_FCS_CRC16) {
-			fcs = crc16(0, (u8 *)skb->data,
-						skb->len - L2CAP_FCS_SIZE);
-			put_unaligned_le16(fcs,
-					skb->data + skb->len - L2CAP_FCS_SIZE);
+			u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
+			put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
 		}
 
 		l2cap_do_send(chan, skb);
 
+		BT_DBG("Sent txseq %d", (int)control->txseq);
+
 		chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
+		chan->frames_sent++;
 	}
+
+	return 0;
 }
 
 static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
@@ -2133,13 +2149,11 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 		if (err)
 			break;
 
-		if (chan->mode == L2CAP_MODE_ERTM) {
+		if (chan->mode == L2CAP_MODE_ERTM)
 			err = l2cap_tx(chan, 0, &seg_queue,
 				       L2CAP_EV_DATA_REQUEST);
-		} else {
-			skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
-			l2cap_streaming_send(chan);
-		}
+		else
+			err = l2cap_streaming_send(chan, &seg_queue);
 
 		if (!err)
 			err = len;
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 04/25] Bluetooth: Refactor l2cap_ertm_send
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (2 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 03/25] Bluetooth: Refactor l2cap_streaming_send Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 05/25] Bluetooth: Refactor l2cap_send_sframe Mat Martineau
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

The new implementation is aware of the new transmit state machine, and
uses struct l2cap_ctrl to compose ERTM headers.  It also has improved
error handling for allocation failures, and does not send the packet
until after all skb and channel data structures are updated.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   79 ++++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 40 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 93c7e67..fce87e5 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1738,9 +1738,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
 static int l2cap_ertm_send(struct l2cap_chan *chan)
 {
 	struct sk_buff *skb, *tx_skb;
-	u16 fcs;
-	u32 control;
-	int nsent = 0;
+	struct l2cap_ctrl *control;
+	int sent = 0;
+
+	BT_DBG("chan %p", chan);
 
 	if (chan->state != BT_CONNECTED)
 		return -ENOTCONN;
@@ -1748,61 +1749,57 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
 		return 0;
 
-	while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
+	while (chan->tx_send_head &&
+	       chan->unacked_frames < chan->remote_tx_win &&
+	       chan->tx_state == L2CAP_TX_STATE_XMIT) {
 
-		if (bt_cb(skb)->control.retries == chan->remote_max_tx &&
-		    chan->remote_max_tx) {
-			l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
-			break;
-		}
+		skb = chan->tx_send_head;
 
-		tx_skb = skb_clone(skb, GFP_ATOMIC);
-
-		bt_cb(skb)->control.retries++;
-
-		control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
-		control &= __get_sar_mask(chan);
+		bt_cb(skb)->control.retries = 1;
+		control = &bt_cb(skb)->control;
 
 		if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-			control |= __set_ctrl_final(chan);
+			control->final = 1;
 
-		control |= __set_reqseq(chan, chan->buffer_seq);
-		control |= __set_txseq(chan, chan->next_tx_seq);
-		control |= __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
+		control->reqseq = chan->buffer_seq;
+		chan->last_acked_seq = chan->buffer_seq;
+		control->txseq = chan->next_tx_seq;
 
-		__put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
+		__pack_control(chan, control, skb);
 
 		if (chan->fcs == L2CAP_FCS_CRC16) {
-			fcs = crc16(0, (u8 *)skb->data,
-						tx_skb->len - L2CAP_FCS_SIZE);
-			put_unaligned_le16(fcs, skb->data +
-						tx_skb->len - L2CAP_FCS_SIZE);
+			u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
+			put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
 		}
 
-		l2cap_do_send(chan, tx_skb);
+		/* Clone after data has been modified. Data is assumed to be
+		   read-only (for locking purposes) on cloned sk_buffs.
+		 */
+		tx_skb = skb_clone(skb, GFP_KERNEL);
+
+		if (!tx_skb)
+			break;
 
 		__set_retrans_timer(chan);
 
-		bt_cb(skb)->control.txseq = chan->next_tx_seq;
-
 		chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
-
-		if (bt_cb(skb)->control.retries == 1) {
-			chan->unacked_frames++;
-
-			if (!nsent++)
-				__clear_ack_timer(chan);
-		}
-
+		chan->unacked_frames++;
 		chan->frames_sent++;
+		sent++;
 
 		if (skb_queue_is_last(&chan->tx_q, skb))
 			chan->tx_send_head = NULL;
 		else
 			chan->tx_send_head = skb_queue_next(&chan->tx_q, skb);
+
+		l2cap_do_send(chan, tx_skb);
+		BT_DBG("Sent txseq %d", (int)control->txseq);
 	}
 
-	return nsent;
+	BT_DBG("Sent %d, %d unacked, %d in ERTM queue", sent,
+	       (int) chan->unacked_frames, skb_queue_len(&chan->tx_q));
+
+	return sent;
 }
 
 static int l2cap_retransmit_frames(struct l2cap_chan *chan)
@@ -2006,7 +2003,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
 	lh->cid = cpu_to_le16(chan->dcid);
 	lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
 
-	__put_control(chan, 0, skb_put(skb, __ctrl_size(chan)));
+	/* Control header is populated later */
+	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+		put_unaligned_le32(0, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
+	else
+		put_unaligned_le16(0, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
 
 	if (sdulen)
 		put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
@@ -2017,9 +2018,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
 		return ERR_PTR(err);
 	}
 
-	if (chan->fcs == L2CAP_FCS_CRC16)
-		put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE));
-
+	bt_cb(skb)->control.fcs = chan->fcs;
 	bt_cb(skb)->control.retries = 0;
 	return skb;
 }
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 05/25] Bluetooth: Refactor l2cap_send_sframe
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (3 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 04/25] Bluetooth: Refactor l2cap_ertm_send Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 06/25] Bluetooth: Consolidate common receive code for ERTM and streaming mode Mat Martineau
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

The new implementation uses struct l2cap_ctrl to set up the sframe
fields, and also reduces duplicate acks by canceling the ack timer
whenever an RR or RNR frame is sent.  sframe PDU generation is also
split in to a separate function to separate it from the logic related
to the connection state and sframe type.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   87 ++++++++++++++++++++++++++------------------
 1 file changed, 51 insertions(+), 36 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fce87e5..fd645ff 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -846,15 +846,12 @@ static inline void __pack_control(struct l2cap_chan *chan,
 	}
 }
 
-static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
+static struct sk_buff *l2cap_create_sframe_pdu(struct l2cap_chan *chan,
+					       u32 control)
 {
 	struct sk_buff *skb;
 	struct l2cap_hdr *lh;
-	struct l2cap_conn *conn = chan->conn;
-	int count, hlen;
-
-	if (chan->state != BT_CONNECTED)
-		return;
+	int hlen;
 
 	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
 		hlen = L2CAP_EXT_HDR_SIZE;
@@ -864,35 +861,65 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
 	if (chan->fcs == L2CAP_FCS_CRC16)
 		hlen += L2CAP_FCS_SIZE;
 
-	BT_DBG("chan %p, control 0x%8.8x", chan, control);
+	skb = bt_skb_alloc(hlen, GFP_KERNEL);
 
-	count = min_t(unsigned int, conn->mtu, hlen);
-
-	control |= __set_sframe(chan);
-
-	if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-		control |= __set_ctrl_final(chan);
-
-	if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
-		control |= __set_ctrl_poll(chan);
-
-	skb = bt_skb_alloc(count, GFP_ATOMIC);
 	if (!skb)
-		return;
+		return ERR_PTR(-ENOMEM);
 
 	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
 	lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
 	lh->cid = cpu_to_le16(chan->dcid);
 
-	__put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
+	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+		put_unaligned_le32(control, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
+	else
+		put_unaligned_le16(control, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
 
 	if (chan->fcs == L2CAP_FCS_CRC16) {
-		u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
+		u16 fcs = crc16(0, (u8 *)skb->data, skb->len);
 		put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
 	}
 
 	skb->priority = HCI_PRIO_MAX;
-	l2cap_do_send(chan, skb);
+	return skb;
+}
+
+static void l2cap_send_sframe(struct l2cap_chan *chan,
+			      struct l2cap_ctrl *control)
+{
+	struct sk_buff *skb;
+	u32 control_field;
+
+	BT_DBG("chan %p, control %p", chan, control);
+
+	if (!control->sframe)
+		return;
+
+	if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
+	    !control->poll)
+		control->final = 1;
+
+	if (control->super == L2CAP_SUPER_RR)
+		clear_bit(CONN_RNR_SENT, &chan->conn_state);
+	else if (control->super == L2CAP_SUPER_RNR)
+		set_bit(CONN_RNR_SENT, &chan->conn_state);
+
+	if (control->super != L2CAP_SUPER_SREJ) {
+		chan->last_acked_seq = control->reqseq;
+		__clear_ack_timer(chan);
+	}
+
+	BT_DBG("reqseq %d, final %d, poll %d, super %d", control->reqseq,
+	       control->final, control->poll, control->super);
+
+	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+		control_field = __pack_extended_control(control);
+	else
+		control_field = __pack_enhanced_control(control);
+
+	skb = l2cap_create_sframe_pdu(chan, control_field);
+	if (!IS_ERR(skb))
+		l2cap_do_send(chan, skb);
 }
 
 static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
@@ -904,8 +931,6 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
 		control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
 
 	control |= __set_reqseq(chan, chan->buffer_seq);
-
-	l2cap_send_sframe(chan, control);
 }
 
 static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
@@ -1823,7 +1848,6 @@ static void __l2cap_send_ack(struct l2cap_chan *chan)
 	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
 		control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
 		set_bit(CONN_RNR_SENT, &chan->conn_state);
-		l2cap_send_sframe(chan, control);
 		return;
 	}
 
@@ -1831,7 +1855,6 @@ static void __l2cap_send_ack(struct l2cap_chan *chan)
 		return;
 
 	control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-	l2cap_send_sframe(chan, control);
 }
 
 static void l2cap_send_ack(struct l2cap_chan *chan)
@@ -1850,8 +1873,6 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan)
 
 	tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
 	control |= __set_reqseq(chan, tail->tx_seq);
-
-	l2cap_send_sframe(chan, control);
 }
 
 static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
@@ -2256,7 +2277,7 @@ static int l2cap_tx_state_xmit(struct l2cap_chan *chan,
 			local_control.super = L2CAP_SUPER_RR;
 			local_control.poll = 1;
 			local_control.reqseq = chan->buffer_seq;
-			l2cap_send_sframe(chan, 0);
+			l2cap_send_sframe(chan, &local_control);
 
 			chan->retry_count = 1;
 			__set_monitor_timer(chan);
@@ -2330,7 +2351,7 @@ static int l2cap_tx_state_wait_f(struct l2cap_chan *chan,
 			local_control.super = L2CAP_SUPER_RR;
 			local_control.poll = 1;
 			local_control.reqseq = chan->buffer_seq;
-			l2cap_send_sframe(chan, 0);
+			l2cap_send_sframe(chan, &local_control);
 
 			chan->retry_count = 1;
 			__set_monitor_timer(chan);
@@ -4230,7 +4251,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 
 	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
 		control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
-		l2cap_send_sframe(chan, control);
 		set_bit(CONN_RNR_SENT, &chan->conn_state);
 	}
 
@@ -4242,7 +4262,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 	if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
 			chan->frames_sent == 0) {
 		control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-		l2cap_send_sframe(chan, control);
 	}
 }
 
@@ -4401,7 +4420,6 @@ static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
 	control = __set_reqseq(chan, chan->buffer_seq);
 	control |= __set_ctrl_poll(chan);
 	control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-	l2cap_send_sframe(chan, control);
 	chan->retry_count = 1;
 
 	__clear_retrans_timer(chan);
@@ -4465,7 +4483,6 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
 		}
 		control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
 		control |= __set_reqseq(chan, l->tx_seq);
-		l2cap_send_sframe(chan, control);
 		list_del(&l->list);
 		list_add_tail(&l->list, &chan->srej_l);
 	}
@@ -4480,7 +4497,6 @@ static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
 		control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
 		control |= __set_reqseq(chan, chan->expected_tx_seq);
 		l2cap_seq_list_append(&chan->srej_list, chan->expected_tx_seq);
-		l2cap_send_sframe(chan, control);
 
 		new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
 		if (!new)
@@ -4764,7 +4780,6 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_c
 		l2cap_send_srejtail(chan);
 	} else {
 		rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR);
-		l2cap_send_sframe(chan, rx_control);
 	}
 }
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 06/25] Bluetooth: Consolidate common receive code for ERTM and streaming mode
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (4 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 05/25] Bluetooth: Refactor l2cap_send_sframe Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 07/25] Bluetooth: Add streaming mode receive and incoming packet classifier Mat Martineau
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Creates a new l2cap_data_rcv function that combines previous code from
l2cap_ertm_data_rcv and l2cap_data_channel.  This reduces duplicate
code for streaming mode, and sets up a framework for the ERTM receive
state machine.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |  126 ++++++++++++++++++++++----------------------
 1 file changed, 62 insertions(+), 64 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fd645ff..3f6bec9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -789,9 +789,11 @@ static inline void __unpack_control(struct l2cap_chan *chan,
 	if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
 		__unpack_extended_control(get_unaligned_le32(skb->data),
 					  &bt_cb(skb)->control);
+		skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
 	} else {
 		__unpack_enhanced_control(get_unaligned_le16(skb->data),
 					  &bt_cb(skb)->control);
+		skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
 	}
 }
 
@@ -4817,27 +4819,39 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_cont
 	return 0;
 }
 
-static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
+static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
+		    struct sk_buff *skb, u8 event)
 {
-	u32 control;
-	u16 req_seq;
-	int len, next_tx_seq_offset, req_seq_offset;
+	/* Placeholder */
+	return -ENOTSUPP;
+}
+
+static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
+			   struct sk_buff *skb)
+{
+	/* Placeholder */
+	return -ENOTSUPP;
+}
+
+static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+	struct l2cap_ctrl *control = &bt_cb(skb)->control;
+	u16 len;
+	u8 event;
 
 	__unpack_control(chan, skb);
 
-	control = __get_control(chan, skb->data);
-	skb_pull(skb, __ctrl_size(chan));
 	len = skb->len;
 
 	/*
 	 * We can just drop the corrupted I-frame here.
 	 * Receiver will miss it and start proper recovery
-	 * procedures and ask retransmission.
+	 * procedures and ask for retransmission.
 	 */
 	if (l2cap_check_fcs(chan, skb))
 		goto drop;
 
-	if (__is_sar_start(chan, control) && !__is_sframe(chan, control))
+	if (!control->sframe && control->sar == L2CAP_SAR_START)
 		len -= L2CAP_SDULEN_SIZE;
 
 	if (chan->fcs == L2CAP_FCS_CRC16)
@@ -4848,34 +4862,57 @@ static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
 		goto drop;
 	}
 
-	req_seq = __get_reqseq(chan, control);
+	if (!control->sframe) {
+		int err;
 
-	req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq);
+		BT_DBG("iframe sar %d, reqseq %d, final %d, txseq %d",
+		       control->sar, control->reqseq, control->final,
+		       control->txseq);
 
-	next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq,
-						chan->expected_ack_seq);
-
-	/* check for invalid req-seq */
-	if (req_seq_offset > next_tx_seq_offset) {
-		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-		goto drop;
-	}
-
-	if (!__is_sframe(chan, control)) {
-		if (len < 0) {
-			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		/* Validate F-bit - F=0 always valid, F=1 only
+		 * valid in TX WAIT_F
+		 */
+		if (control->final && chan->tx_state != L2CAP_TX_STATE_WAIT_F)
 			goto drop;
+
+		if (chan->mode != L2CAP_MODE_STREAMING) {
+			event = L2CAP_EV_RECV_IFRAME;
+			err = l2cap_rx(chan, control, skb, event);
+		} else {
+			err = l2cap_stream_rx(chan, control, skb);
 		}
 
-		l2cap_data_channel_iframe(chan, control, skb);
+		if (err)
+			l2cap_send_disconn_req(chan->conn, chan,
+					       ECONNRESET);
 	} else {
+		const u8 rx_func_to_event[4] = {
+			L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ,
+			L2CAP_EV_RECV_RNR, L2CAP_EV_RECV_SREJ
+		};
+
+		/* Only I-frames are expected in streaming mode */
+		if (chan->mode == L2CAP_MODE_STREAMING)
+			goto drop;
+
+		BT_DBG("sframe reqseq %d, final %d, poll %d, super %d",
+		       control->reqseq, control->final, control->poll,
+		       control->super);
+
 		if (len != 0) {
 			BT_ERR("%d", len);
 			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
 			goto drop;
 		}
 
-		l2cap_data_channel_sframe(chan, control, skb);
+		/* Validate F and P bits */
+		if (control->final && (control->poll ||
+				       chan->tx_state != L2CAP_TX_STATE_WAIT_F))
+			goto drop;
+
+		event = rx_func_to_event[control->super];
+		if (l2cap_rx(chan, control, skb, event))
+			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
 	}
 
 	return 0;
@@ -4888,9 +4925,6 @@ drop:
 static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
 {
 	struct l2cap_chan *chan;
-	u32 control;
-	u16 tx_seq;
-	int len;
 
 	chan = l2cap_get_chan_by_scid(conn, cid);
 	if (!chan) {
@@ -4920,44 +4954,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
 		break;
 
 	case L2CAP_MODE_ERTM:
-		l2cap_ertm_data_rcv(chan, skb);
-
-		goto done;
-
 	case L2CAP_MODE_STREAMING:
-		control = __get_control(chan, skb->data);
-		skb_pull(skb, __ctrl_size(chan));
-		len = skb->len;
-
-		if (l2cap_check_fcs(chan, skb))
-			goto drop;
-
-		if (__is_sar_start(chan, control))
-			len -= L2CAP_SDULEN_SIZE;
-
-		if (chan->fcs == L2CAP_FCS_CRC16)
-			len -= L2CAP_FCS_SIZE;
-
-		if (len > chan->mps || len < 0 || __is_sframe(chan, control))
-			goto drop;
-
-		tx_seq = __get_txseq(chan, control);
-
-		if (chan->expected_tx_seq != tx_seq) {
-			/* Frame(s) missing - must discard partial SDU */
-			kfree_skb(chan->sdu);
-			chan->sdu = NULL;
-			chan->sdu_last_frag = NULL;
-			chan->sdu_len = 0;
-
-			/* TODO: Notify userland of missing data */
-		}
-
-		chan->expected_tx_seq = __next_seq(chan, tx_seq);
-
-		if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
-			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-
+		l2cap_data_rcv(chan, skb);
 		goto done;
 
 	default:
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 07/25] Bluetooth: Add streaming mode receive and incoming packet classifier
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (5 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 06/25] Bluetooth: Consolidate common receive code for ERTM and streaming mode Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 08/25] Bluetooth: Remove receive code that has been superceded Mat Martineau
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Streaming mode reception is fairly simple, with in-sequence frames
being reassembled as they arrive.  Out-of-sequence frames are dropped,
and also clear any partially-assembled SDUs that may exist.

The packet classifier determines if the txseq value of the incoming
packet is expected, invalid (resulting in a disconnection), invalid
(ignorable), duplicate, or having to do with an SREJ request that was
previously sent.  The rules for each classification are defined in the
ERTM specification, and consolidating these rules in one place helps
clarify the receive state machine.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |  136 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 130 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3f6bec9..50cc36b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2417,6 +2417,13 @@ static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 	return err;
 }
 
+static void l2cap_pass_to_tx(struct l2cap_chan *chan,
+			     struct l2cap_ctrl *control)
+{
+	BT_DBG("chan %p, control %p", chan, control);
+	l2cap_tx(chan, control, 0, L2CAP_EV_RECV_REQSEQ_AND_FBIT);
+}
+
 /* Copy frame to all raw sockets on that connection */
 static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
 {
@@ -4321,11 +4328,12 @@ static void append_skb_frag(struct sk_buff *skb,
 	skb->truesize += new_frag->truesize;
 }
 
-static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control)
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
+				struct l2cap_ctrl *control)
 {
 	int err = -EINVAL;
 
-	switch (__get_ctrl_sar(chan, control)) {
+	switch (control->sar) {
 	case L2CAP_SAR_UNSEGMENTED:
 		if (chan->sdu)
 			break;
@@ -4460,7 +4468,6 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
 
 		skb = skb_dequeue(&chan->srej_q);
 		control = __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
-		err = l2cap_reassemble_sdu(chan, skb, control);
 
 		if (err < 0) {
 			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -4634,7 +4641,6 @@ expected:
 		return 0;
 	}
 
-	err = l2cap_reassemble_sdu(chan, skb, rx_control);
 	chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
 
 	if (err < 0) {
@@ -4819,6 +4825,93 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_cont
 	return 0;
 }
 
+static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
+{
+	BT_DBG("chan %p, txseq %d", chan, txseq);
+
+	BT_DBG("last_acked_seq %d, expected_tx_seq %d", chan->last_acked_seq,
+	       chan->expected_tx_seq);
+
+	if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
+		if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
+								chan->tx_win) {
+			/* See notes below regarding "double poll" and
+			 * invalid packets.
+			 */
+			if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
+				BT_DBG("Invalid/Ignore - after SREJ");
+				return L2CAP_TXSEQ_INVALID_IGNORE;
+			} else {
+				BT_DBG("Invalid - in window after SREJ sent");
+				return L2CAP_TXSEQ_INVALID;
+			}
+		}
+
+		if (chan->srej_list.head == txseq) {
+			BT_DBG("Expected SREJ");
+			return L2CAP_TXSEQ_EXPECTED_SREJ;
+		}
+
+		if (l2cap_ertm_seq_in_queue(&chan->srej_q, txseq)) {
+			BT_DBG("Duplicate SREJ - txseq already stored");
+			return L2CAP_TXSEQ_DUPLICATE_SREJ;
+		}
+
+		if (l2cap_seq_list_contains(&chan->srej_list, txseq)) {
+			BT_DBG("Unexpected SREJ - not requested");
+			return L2CAP_TXSEQ_UNEXPECTED_SREJ;
+		}
+	}
+
+	if (chan->expected_tx_seq == txseq) {
+		if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
+		    chan->tx_win) {
+			BT_DBG("Invalid - txseq outside tx window");
+			return L2CAP_TXSEQ_INVALID;
+		} else {
+			BT_DBG("Expected");
+			return L2CAP_TXSEQ_EXPECTED;
+		}
+	}
+
+	if (__seq_offset(chan, txseq, chan->last_acked_seq) <
+		__seq_offset(chan, chan->expected_tx_seq,
+			     chan->last_acked_seq)){
+		BT_DBG("Duplicate - expected_tx_seq later than txseq");
+		return L2CAP_TXSEQ_DUPLICATE;
+	}
+
+	if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) {
+		/* A source of invalid packets is a "double poll" condition,
+		 * where delays cause us to send multiple poll packets.  If
+		 * the remote stack receives and processes both polls,
+		 * sequence numbers can wrap around in such a way that a
+		 * resent frame has a sequence number that looks like new data
+		 * with a sequence gap.  This would trigger an erroneous SREJ
+		 * request.
+		 *
+		 * Fortunately, this is impossible with a tx window that's
+		 * less than half of the maximum sequence number, which allows
+		 * invalid frames to be safely ignored.
+		 *
+		 * With tx window sizes greater than half of the tx window
+		 * maximum, the frame is invalid and cannot be ignored.  This
+		 * causes a disconnect.
+		 */
+
+		if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
+			BT_DBG("Invalid/Ignore - txseq outside tx window");
+			return L2CAP_TXSEQ_INVALID_IGNORE;
+		} else {
+			BT_DBG("Invalid - txseq outside tx window");
+			return L2CAP_TXSEQ_INVALID;
+		}
+	} else {
+		BT_DBG("Unexpected - txseq indicates missing frames");
+		return L2CAP_TXSEQ_UNEXPECTED;
+	}
+}
+
 static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 		    struct sk_buff *skb, u8 event)
 {
@@ -4829,8 +4922,39 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 			   struct sk_buff *skb)
 {
-	/* Placeholder */
-	return -ENOTSUPP;
+	int err = 0;
+
+	BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
+	       chan->rx_state);
+
+	if (l2cap_classify_txseq(chan, control->txseq) ==
+	    L2CAP_TXSEQ_EXPECTED) {
+		l2cap_pass_to_tx(chan, control);
+
+		BT_DBG("buffer_seq %d->%d", chan->buffer_seq,
+		       __next_seq(chan, chan->buffer_seq));
+
+		chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
+
+		l2cap_reassemble_sdu(chan, skb, control);
+	} else {
+		if (chan->sdu) {
+			kfree_skb(chan->sdu);
+			chan->sdu = NULL;
+		}
+		chan->sdu_last_frag = NULL;
+		chan->sdu_len = 0;
+
+		if (skb) {
+			BT_DBG("Freeing %p", skb);
+			kfree_skb(skb);
+		}
+	}
+
+	chan->last_acked_seq = control->txseq;
+	chan->expected_tx_seq = __next_seq(chan, control->txseq);
+
+	return err;
 }
 
 static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 08/25] Bluetooth: Remove receive code that has been superceded
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (6 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 07/25] Bluetooth: Add streaming mode receive and incoming packet classifier Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 09/25] Bluetooth: Refactor l2cap_send_ack Mat Martineau
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This deletes the receive code that had handlers for each frame type at
the top level, and then had logic to determine the receive state
within each handler.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 include/net/bluetooth/l2cap.h |    8 -
 net/bluetooth/l2cap_core.c    |  492 -----------------------------------------
 2 files changed, 500 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 452fcc4..7d1da5a 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -419,11 +419,6 @@ struct l2cap_seq_list {
 #define L2CAP_SEQ_LIST_CLEAR	0xFFFF
 #define L2CAP_SEQ_LIST_TAIL	0x8000
 
-struct srej_list {
-	__u16	tx_seq;
-	struct list_head list;
-};
-
 struct l2cap_chan {
 	struct sock *sk;
 
@@ -475,14 +470,12 @@ struct l2cap_chan {
 	__u16		expected_ack_seq;
 	__u16		expected_tx_seq;
 	__u16		buffer_seq;
-	__u16		buffer_seq_srej;
 	__u16		srej_save_reqseq;
 	__u16		last_acked_seq;
 	__u16		frames_sent;
 	__u16		unacked_frames;
 	__u8		retry_count;
 	__u16		srej_queue_next;
-	__u8		num_acked;
 	__u16		sdu_len;
 	struct sk_buff	*sdu;
 	struct sk_buff	*sdu_last_frag;
@@ -515,7 +508,6 @@ struct l2cap_chan {
 	struct sk_buff_head	srej_q;
 	struct l2cap_seq_list	srej_list;
 	struct l2cap_seq_list	retrans_list;
-	struct list_head	srej_l;
 
 	struct list_head	list;
 	struct list_head	global_l;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 50cc36b..ce8bffd 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -532,8 +532,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
 	skb_queue_purge(&chan->tx_q);
 
 	if (chan->mode == L2CAP_MODE_ERTM) {
-		struct srej_list *l, *tmp;
-
 		__clear_retrans_timer(chan);
 		__clear_monitor_timer(chan);
 		__clear_ack_timer(chan);
@@ -542,10 +540,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
 
 		l2cap_seq_list_free(&chan->srej_list);
 		l2cap_seq_list_free(&chan->retrans_list);
-		list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
-			list_del(&l->list);
-			kfree(l);
-		}
 	}
 }
 
@@ -1655,25 +1649,6 @@ static void l2cap_retrans_timeout(struct work_struct *work)
 	l2cap_chan_put(chan);
 }
 
-static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_peek(&chan->tx_q)) &&
-			chan->unacked_frames) {
-		if (bt_cb(skb)->control.txseq == chan->expected_ack_seq)
-			break;
-
-		skb = skb_dequeue(&chan->tx_q);
-		kfree_skb(skb);
-
-		chan->unacked_frames--;
-	}
-
-	if (!chan->unacked_frames)
-		__clear_retrans_timer(chan);
-}
-
 static int l2cap_streaming_send(struct l2cap_chan *chan,
 				struct sk_buff_head *skbs)
 {
@@ -1715,53 +1690,6 @@ static int l2cap_streaming_send(struct l2cap_chan *chan,
 	return 0;
 }
 
-static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
-{
-	struct sk_buff *skb, *tx_skb;
-	u16 fcs;
-	u32 control;
-
-	skb = skb_peek(&chan->tx_q);
-	if (!skb)
-		return;
-
-	while (bt_cb(skb)->control.txseq != tx_seq) {
-		if (skb_queue_is_last(&chan->tx_q, skb))
-			return;
-
-		skb = skb_queue_next(&chan->tx_q, skb);
-	}
-
-	if (bt_cb(skb)->control.retries == chan->remote_max_tx &&
-	    chan->remote_max_tx) {
-		l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
-		return;
-	}
-
-	tx_skb = skb_clone(skb, GFP_ATOMIC);
-	bt_cb(skb)->control.retries++;
-
-	control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
-	control &= __get_sar_mask(chan);
-
-	if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
-		control |= __set_ctrl_final(chan);
-
-	control |= __set_reqseq(chan, chan->buffer_seq);
-	control |= __set_txseq(chan, tx_seq);
-
-	__put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
-
-	if (chan->fcs == L2CAP_FCS_CRC16) {
-		fcs = crc16(0, (u8 *)tx_skb->data,
-						tx_skb->len - L2CAP_FCS_SIZE);
-		put_unaligned_le16(fcs,
-				tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE);
-	}
-
-	l2cap_do_send(chan, tx_skb);
-}
-
 static int l2cap_ertm_send(struct l2cap_chan *chan)
 {
 	struct sk_buff *skb, *tx_skb;
@@ -1865,18 +1793,6 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
 	__l2cap_send_ack(chan);
 }
 
-static void l2cap_send_srejtail(struct l2cap_chan *chan)
-{
-	struct srej_list *tail;
-	u32 control;
-
-	control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
-	control |= __set_ctrl_final(chan);
-
-	tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
-	control |= __set_reqseq(chan, tail->tx_seq);
-}
-
 static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
 					 struct msghdr *msg, int len,
 					 int count, struct sk_buff *skb)
@@ -2636,7 +2552,6 @@ static inline int l2cap_ertm_init(struct l2cap_chan *chan)
 	chan->expected_ack_seq = 0;
 	chan->unacked_frames = 0;
 	chan->buffer_seq = 0;
-	chan->num_acked = 0;
 	chan->frames_sent = 0;
 	chan->last_acked_seq = 0;
 	chan->sdu = NULL;
@@ -2657,7 +2572,6 @@ static inline int l2cap_ertm_init(struct l2cap_chan *chan)
 
 	skb_queue_head_init(&chan->srej_q);
 
-	INIT_LIST_HEAD(&chan->srej_l);
 	err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win);
 	if (err < 0)
 		return err;
@@ -4274,41 +4188,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 	}
 }
 
-static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar)
-{
-	struct sk_buff *next_skb;
-	int tx_seq_offset, next_tx_seq_offset;
-
-	bt_cb(skb)->control.txseq = tx_seq;
-	bt_cb(skb)->control.sar = sar;
-
-	next_skb = skb_peek(&chan->srej_q);
-
-	tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
-
-	while (next_skb) {
-		if (bt_cb(next_skb)->control.txseq == tx_seq)
-			return -EINVAL;
-
-		next_tx_seq_offset = __seq_offset(chan,
-			bt_cb(next_skb)->control.txseq, chan->buffer_seq);
-
-		if (next_tx_seq_offset > tx_seq_offset) {
-			__skb_queue_before(&chan->srej_q, next_skb, skb);
-			return 0;
-		}
-
-		if (skb_queue_is_last(&chan->srej_q, next_skb))
-			next_skb = NULL;
-		else
-			next_skb = skb_queue_next(&chan->srej_q, next_skb);
-	}
-
-	__skb_queue_tail(&chan->srej_q, skb);
-
-	return 0;
-}
-
 static void append_skb_frag(struct sk_buff *skb,
 			struct sk_buff *new_frag, struct sk_buff **last_frag)
 {
@@ -4454,377 +4333,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
 	}
 }
 
-static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
-{
-	struct sk_buff *skb;
-	u32 control;
-
-	while ((skb = skb_peek(&chan->srej_q)) &&
-			!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-		int err;
-
-		if (bt_cb(skb)->control.txseq != tx_seq)
-			break;
-
-		skb = skb_dequeue(&chan->srej_q);
-		control = __set_ctrl_sar(chan, bt_cb(skb)->control.sar);
-
-		if (err < 0) {
-			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-			break;
-		}
-
-		chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej);
-		tx_seq = __next_seq(chan, tx_seq);
-	}
-}
-
-static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
-{
-	struct srej_list *l, *tmp;
-	u32 control;
-
-	list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
-		if (l->tx_seq == tx_seq) {
-			list_del(&l->list);
-			kfree(l);
-			return;
-		}
-		control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
-		control |= __set_reqseq(chan, l->tx_seq);
-		list_del(&l->list);
-		list_add_tail(&l->list, &chan->srej_l);
-	}
-}
-
-static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
-{
-	struct srej_list *new;
-	u32 control;
-
-	while (tx_seq != chan->expected_tx_seq) {
-		control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
-		control |= __set_reqseq(chan, chan->expected_tx_seq);
-		l2cap_seq_list_append(&chan->srej_list, chan->expected_tx_seq);
-
-		new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
-		if (!new)
-			return -ENOMEM;
-
-		new->tx_seq = chan->expected_tx_seq;
-
-		chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
-
-		list_add_tail(&new->list, &chan->srej_l);
-	}
-
-	chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
-
-	return 0;
-}
-
-static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
-{
-	u16 tx_seq = __get_txseq(chan, rx_control);
-	u16 req_seq = __get_reqseq(chan, rx_control);
-	u8 sar = __get_ctrl_sar(chan, rx_control);
-	int tx_seq_offset, expected_tx_seq_offset;
-	int num_to_ack = (chan->tx_win/6) + 1;
-	int err = 0;
-
-	BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len,
-							tx_seq, rx_control);
-
-	if (__is_ctrl_final(chan, rx_control) &&
-			test_bit(CONN_WAIT_F, &chan->conn_state)) {
-		__clear_monitor_timer(chan);
-		if (chan->unacked_frames > 0)
-			__set_retrans_timer(chan);
-		clear_bit(CONN_WAIT_F, &chan->conn_state);
-	}
-
-	chan->expected_ack_seq = req_seq;
-	l2cap_drop_acked_frames(chan);
-
-	tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
-
-	/* invalid tx_seq */
-	if (tx_seq_offset >= chan->tx_win) {
-		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-		goto drop;
-	}
-
-	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-		if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
-			l2cap_send_ack(chan);
-		goto drop;
-	}
-
-	if (tx_seq == chan->expected_tx_seq)
-		goto expected;
-
-	if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
-		struct srej_list *first;
-
-		first = list_first_entry(&chan->srej_l,
-				struct srej_list, list);
-		if (tx_seq == first->tx_seq) {
-			l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
-			l2cap_check_srej_gap(chan, tx_seq);
-
-			list_del(&first->list);
-			kfree(first);
-
-			if (list_empty(&chan->srej_l)) {
-				chan->buffer_seq = chan->buffer_seq_srej;
-				clear_bit(CONN_SREJ_SENT, &chan->conn_state);
-				l2cap_send_ack(chan);
-				BT_DBG("chan %p, Exit SREJ_SENT", chan);
-			}
-		} else {
-			struct srej_list *l;
-
-			/* duplicated tx_seq */
-			if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0)
-				goto drop;
-
-			list_for_each_entry(l, &chan->srej_l, list) {
-				if (l->tx_seq == tx_seq) {
-					l2cap_resend_srejframe(chan, tx_seq);
-					return 0;
-				}
-			}
-
-			err = l2cap_send_srejframe(chan, tx_seq);
-			if (err < 0) {
-				l2cap_send_disconn_req(chan->conn, chan, -err);
-				return err;
-			}
-		}
-	} else {
-		expected_tx_seq_offset = __seq_offset(chan,
-				chan->expected_tx_seq, chan->buffer_seq);
-
-		/* duplicated tx_seq */
-		if (tx_seq_offset < expected_tx_seq_offset)
-			goto drop;
-
-		set_bit(CONN_SREJ_SENT, &chan->conn_state);
-
-		BT_DBG("chan %p, Enter SREJ", chan);
-
-		INIT_LIST_HEAD(&chan->srej_l);
-		chan->buffer_seq_srej = chan->buffer_seq;
-
-		__skb_queue_head_init(&chan->srej_q);
-		l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
-
-		/* Set P-bit only if there are some I-frames to ack. */
-		if (__clear_ack_timer(chan))
-			set_bit(CONN_SEND_PBIT, &chan->conn_state);
-
-		err = l2cap_send_srejframe(chan, tx_seq);
-		if (err < 0) {
-			l2cap_send_disconn_req(chan->conn, chan, -err);
-			return err;
-		}
-	}
-	return 0;
-
-expected:
-	chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
-
-	if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
-		bt_cb(skb)->control.txseq = tx_seq;
-		bt_cb(skb)->control.sar = sar;
-		__skb_queue_tail(&chan->srej_q, skb);
-		return 0;
-	}
-
-	chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
-
-	if (err < 0) {
-		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
-		return err;
-	}
-
-	if (__is_ctrl_final(chan, rx_control)) {
-		if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
-			l2cap_retransmit_frames(chan);
-	}
-
-
-	chan->num_acked = (chan->num_acked + 1) % num_to_ack;
-	if (chan->num_acked == num_to_ack - 1)
-		l2cap_send_ack(chan);
-	else
-		__set_ack_timer(chan);
-
-	return 0;
-
-drop:
-	kfree_skb(skb);
-	return 0;
-}
-
-static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control)
-{
-	BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan,
-				__get_reqseq(chan, rx_control), rx_control);
-
-	chan->expected_ack_seq = __get_reqseq(chan, rx_control);
-	l2cap_drop_acked_frames(chan);
-
-	if (__is_ctrl_poll(chan, rx_control)) {
-		set_bit(CONN_SEND_FBIT, &chan->conn_state);
-		if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
-			if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
-					(chan->unacked_frames > 0))
-				__set_retrans_timer(chan);
-
-			clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-			l2cap_send_srejtail(chan);
-		} else {
-			l2cap_send_i_or_rr_or_rnr(chan);
-		}
-
-	} else if (__is_ctrl_final(chan, rx_control)) {
-		clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-
-		if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
-			l2cap_retransmit_frames(chan);
-
-	} else {
-		if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
-				(chan->unacked_frames > 0))
-			__set_retrans_timer(chan);
-
-		clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-		if (test_bit(CONN_SREJ_SENT, &chan->conn_state))
-			l2cap_send_ack(chan);
-		else
-			l2cap_ertm_send(chan);
-	}
-}
-
-static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control)
-{
-	u16 tx_seq = __get_reqseq(chan, rx_control);
-
-	BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
-
-	clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-
-	chan->expected_ack_seq = tx_seq;
-	l2cap_drop_acked_frames(chan);
-
-	if (__is_ctrl_final(chan, rx_control)) {
-		if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
-			l2cap_retransmit_frames(chan);
-	} else {
-		l2cap_retransmit_frames(chan);
-
-		if (test_bit(CONN_WAIT_F, &chan->conn_state))
-			set_bit(CONN_REJ_ACT, &chan->conn_state);
-	}
-}
-static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control)
-{
-	u16 tx_seq = __get_reqseq(chan, rx_control);
-
-	BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
-
-	clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-
-	if (__is_ctrl_poll(chan, rx_control)) {
-		chan->expected_ack_seq = tx_seq;
-		l2cap_drop_acked_frames(chan);
-
-		set_bit(CONN_SEND_FBIT, &chan->conn_state);
-		l2cap_retransmit_one_frame(chan, tx_seq);
-
-		l2cap_ertm_send(chan);
-
-		if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
-			chan->srej_save_reqseq = tx_seq;
-			set_bit(CONN_SREJ_ACT, &chan->conn_state);
-		}
-	} else if (__is_ctrl_final(chan, rx_control)) {
-		if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
-				chan->srej_save_reqseq == tx_seq)
-			clear_bit(CONN_SREJ_ACT, &chan->conn_state);
-		else
-			l2cap_retransmit_one_frame(chan, tx_seq);
-	} else {
-		l2cap_retransmit_one_frame(chan, tx_seq);
-		if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
-			chan->srej_save_reqseq = tx_seq;
-			set_bit(CONN_SREJ_ACT, &chan->conn_state);
-		}
-	}
-}
-
-static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control)
-{
-	u16 tx_seq = __get_reqseq(chan, rx_control);
-
-	BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
-
-	set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
-	chan->expected_ack_seq = tx_seq;
-	l2cap_drop_acked_frames(chan);
-
-	if (__is_ctrl_poll(chan, rx_control))
-		set_bit(CONN_SEND_FBIT, &chan->conn_state);
-
-	if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
-		__clear_retrans_timer(chan);
-		if (__is_ctrl_poll(chan, rx_control))
-			l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
-		return;
-	}
-
-	if (__is_ctrl_poll(chan, rx_control)) {
-		l2cap_send_srejtail(chan);
-	} else {
-		rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR);
-	}
-}
-
-static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
-{
-	BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len);
-
-	if (__is_ctrl_final(chan, rx_control) &&
-			test_bit(CONN_WAIT_F, &chan->conn_state)) {
-		__clear_monitor_timer(chan);
-		if (chan->unacked_frames > 0)
-			__set_retrans_timer(chan);
-		clear_bit(CONN_WAIT_F, &chan->conn_state);
-	}
-
-	switch (__get_ctrl_super(chan, rx_control)) {
-	case L2CAP_SUPER_RR:
-		l2cap_data_channel_rrframe(chan, rx_control);
-		break;
-
-	case L2CAP_SUPER_REJ:
-		l2cap_data_channel_rejframe(chan, rx_control);
-		break;
-
-	case L2CAP_SUPER_SREJ:
-		l2cap_data_channel_srejframe(chan, rx_control);
-		break;
-
-	case L2CAP_SUPER_RNR:
-		l2cap_data_channel_rnrframe(chan, rx_control);
-		break;
-	}
-
-	kfree_skb(skb);
-	return 0;
-}
-
 static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
 {
 	BT_DBG("chan %p, txseq %d", chan, txseq);
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 09/25] Bluetooth: Refactor l2cap_send_ack
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (7 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 08/25] Bluetooth: Remove receive code that has been superceded Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 10/25] Bluetooth: Use the transmit state machine for busy state changes Mat Martineau
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

The function now encapsulates more of the logic to either immediately
send an ack if the transmit window is over 75% full, or wait for the
ack timer to expire if the transmit window is not full enough.  It is
also able to push out waiting iframes that can carry an
acknowledgement.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   68 ++++++++++++++++++++++++++++++--------------
 1 file changed, 47 insertions(+), 21 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ce8bffd..9c3f51c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1769,28 +1769,54 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
 	return ret;
 }
 
-static void __l2cap_send_ack(struct l2cap_chan *chan)
-{
-	u32 control = 0;
-
-	control |= __set_reqseq(chan, chan->buffer_seq);
-
-	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-		control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
-		set_bit(CONN_RNR_SENT, &chan->conn_state);
-		return;
-	}
-
-	if (l2cap_ertm_send(chan) > 0)
-		return;
-
-	control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-}
-
 static void l2cap_send_ack(struct l2cap_chan *chan)
 {
-	__clear_ack_timer(chan);
-	__l2cap_send_ack(chan);
+	struct l2cap_ctrl control;
+	u16 frames_to_ack = __seq_offset(chan, chan->buffer_seq,
+					 chan->last_acked_seq);
+	int threshold;
+
+	BT_DBG("chan %p last_acked_seq %d buffer_seq %d",
+	       chan, chan->last_acked_seq, chan->buffer_seq);
+
+	memset(&control, 0, sizeof(control));
+	control.sframe = 1;
+
+	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
+	    chan->rx_state == L2CAP_RX_STATE_RECV) {
+		__clear_ack_timer(chan);
+		control.super = L2CAP_SUPER_RNR;
+		control.reqseq = chan->buffer_seq;
+		l2cap_send_sframe(chan, &control);
+	} else {
+		if (!test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) {
+			l2cap_ertm_send(chan);
+			/* If any i-frames were sent, they included an ack */
+			if (chan->buffer_seq == chan->last_acked_seq)
+				frames_to_ack = 0;
+		}
+
+		/* Ack now if the tx window is 3/4ths full.
+		 * Calculate without mul or div
+		 */
+		threshold = chan->tx_win;
+		threshold += threshold << 1;
+		threshold >>= 2;
+
+		BT_DBG("frames_to_ack %d, threshold %d", (int)frames_to_ack,
+		       threshold);
+
+		if (frames_to_ack >= threshold) {
+			__clear_ack_timer(chan);
+			control.super = L2CAP_SUPER_RR;
+			control.reqseq = chan->buffer_seq;
+			l2cap_send_sframe(chan, &control);
+			frames_to_ack = 0;
+		}
+
+		if (frames_to_ack)
+			__set_ack_timer(chan);
+	}
 }
 
 static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
@@ -2536,7 +2562,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
 
 	l2cap_chan_lock(chan);
 
-	__l2cap_send_ack(chan);
+	l2cap_send_ack(chan);
 
 	l2cap_chan_unlock(chan);
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 10/25] Bluetooth: Use the transmit state machine for busy state changes
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (8 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 09/25] Bluetooth: Refactor l2cap_send_ack Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 11/25] Bluetooth: Update l2cap_send_i_or_rr_or_rnr to fit the spec better Mat Martineau
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This lets the transmit state machine handle local busy state changes,
since different actions are taken in the different transmit states.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   47 +++++++-------------------------------------
 1 file changed, 7 insertions(+), 40 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9c3f51c..0f130f7 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4315,48 +4315,15 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
 	return err;
 }
 
-static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
-{
-	BT_DBG("chan %p, Enter local busy", chan);
-
-	set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
-	l2cap_seq_list_clear(&chan->srej_list);
-
-	__set_ack_timer(chan);
-}
-
-static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
-{
-	u32 control;
-
-	if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
-		goto done;
-
-	control = __set_reqseq(chan, chan->buffer_seq);
-	control |= __set_ctrl_poll(chan);
-	control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
-	chan->retry_count = 1;
-
-	__clear_retrans_timer(chan);
-	__set_monitor_timer(chan);
-
-	set_bit(CONN_WAIT_F, &chan->conn_state);
-
-done:
-	clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
-	clear_bit(CONN_RNR_SENT, &chan->conn_state);
-
-	BT_DBG("chan %p, Exit local busy", chan);
-}
-
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
 {
-	if (chan->mode == L2CAP_MODE_ERTM) {
-		if (busy)
-			l2cap_ertm_enter_local_busy(chan);
-		else
-			l2cap_ertm_exit_local_busy(chan);
-	}
+	u8 event;
+
+	if (chan->mode != L2CAP_MODE_ERTM)
+		return;
+
+	event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR;
+	l2cap_tx(chan, 0, 0, event);
 }
 
 static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 11/25] Bluetooth: Update l2cap_send_i_or_rr_or_rnr to fit the spec better
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (9 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 10/25] Bluetooth: Use the transmit state machine for busy state changes Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 12/25] Bluetooth: Add the ERTM receive state machine Mat Martineau
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This action now exactly matches what is defined in the ERTM
specification, including clearing the remote busy flag and setting the
retransmit timer rather than retransmitting frames directly.  The spec
does not retransmit frames as part of this action, since
retransmission is only triggered by REJ, SREJ, or an RR with the final
bit set.  struct l2cap_ctrl is also used to set up header values.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0f130f7..3a10a30 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1757,18 +1757,6 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 	return sent;
 }
 
-static int l2cap_retransmit_frames(struct l2cap_chan *chan)
-{
-	int ret;
-
-	if (!skb_queue_empty(&chan->tx_q))
-		chan->tx_send_head = chan->tx_q.next;
-
-	chan->next_tx_seq = chan->expected_ack_seq;
-	ret = l2cap_ertm_send(chan);
-	return ret;
-}
-
 static void l2cap_send_ack(struct l2cap_chan *chan)
 {
 	struct l2cap_ctrl control;
@@ -4192,25 +4180,35 @@ static int l2cap_check_fcs(struct l2cap_chan *chan,  struct sk_buff *skb)
 
 static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 {
-	u32 control = 0;
+	struct l2cap_ctrl control;
 
-	chan->frames_sent = 0;
+	BT_DBG("chan %p", chan);
 
-	control |= __set_reqseq(chan, chan->buffer_seq);
+	memset(&control, 0, sizeof(control));
+	control.sframe = 1;
+	control.final = 1;
+	control.reqseq = chan->buffer_seq;
+	set_bit(CONN_SEND_FBIT, &chan->conn_state);
 
 	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-		control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
-		set_bit(CONN_RNR_SENT, &chan->conn_state);
+		control.super = L2CAP_SUPER_RNR;
+		l2cap_send_sframe(chan, &control);
 	}
 
-	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
-		l2cap_retransmit_frames(chan);
+	if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
+	    chan->unacked_frames > 0)
+		__set_retrans_timer(chan);
 
+	/* Send pending iframes */
 	l2cap_ertm_send(chan);
 
 	if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
-			chan->frames_sent == 0) {
-		control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
+	    test_bit(CONN_SEND_FBIT, &chan->conn_state)) {
+		/* F-bit wasn't sent in an s-frame or i-frame yet, so
+		 * send it now.
+		 */
+		control.super = L2CAP_SUPER_RR;
+		l2cap_send_sframe(chan, &control);
 	}
 }
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 12/25] Bluetooth: Add the ERTM receive state machine
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (10 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 11/25] Bluetooth: Update l2cap_send_i_or_rr_or_rnr to fit the spec better Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 13/25] Bluetooth: Add implementation for retransmitting all unacked frames Mat Martineau
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This adds a top-level state machine with handlers for two receive
states defined in the ERTM spec, RECV and SREJ_SENT.  The reqseq value
of the incoming frame is also validated at the top level and a
disconnection is forced if it is invalid.  The actions for the RECV
and SREJ_SENT states are implemented according to the state tables in
the ERTM specification.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |  355 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 353 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3a10a30..86c4253 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1757,6 +1757,12 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 	return sent;
 }
 
+static void l2cap_retransmit_all(struct l2cap_chan *chan,
+				 struct l2cap_ctrl *control)
+{
+	/* Placeholder */
+}
+
 static void l2cap_send_ack(struct l2cap_chan *chan)
 {
 	struct l2cap_ctrl control;
@@ -2124,6 +2130,21 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 	return err;
 }
 
+static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
+{
+	/* Placeholder */
+}
+
+static void l2cap_send_srej_tail(struct l2cap_chan *chan)
+{
+	/* Placeholder */
+}
+
+static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq)
+{
+	/* Placeholder */
+}
+
 static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
 {
 	struct sk_buff *acked_skb;
@@ -4324,6 +4345,24 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
 	l2cap_tx(chan, 0, 0, event);
 }
 
+static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
+{
+	/* Placeholder */
+	return 0;
+}
+
+static void l2cap_handle_srej(struct l2cap_chan *chan,
+			      struct l2cap_ctrl *control)
+{
+	/* Placeholder */
+}
+
+static void l2cap_handle_rej(struct l2cap_chan *chan,
+			     struct l2cap_ctrl *control)
+{
+	/* Placeholder */
+}
+
 static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
 {
 	BT_DBG("chan %p, txseq %d", chan, txseq);
@@ -4411,11 +4450,323 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
 	}
 }
 
+static int l2cap_rx_state_recv(struct l2cap_chan *chan,
+			       struct l2cap_ctrl *control,
+			       struct sk_buff *skb, u8 event)
+{
+	int err = 0;
+	bool skb_in_use = 0;
+
+	BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+	       event);
+
+	switch (event) {
+	case L2CAP_EV_RECV_IFRAME:
+		switch (l2cap_classify_txseq(chan, control->txseq)) {
+		case L2CAP_TXSEQ_EXPECTED:
+			l2cap_pass_to_tx(chan, control);
+
+			if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+				BT_DBG("Busy, discarding expected seq %d",
+				       control->txseq);
+				break;
+			}
+
+			chan->expected_tx_seq = __next_seq(chan,
+							   control->txseq);
+
+			chan->buffer_seq = chan->expected_tx_seq;
+			skb_in_use = 1;
+
+			err = l2cap_reassemble_sdu(chan, skb, control);
+			if (err)
+				break;
+
+			if (control->final) {
+				if (!test_and_clear_bit(CONN_REJ_ACT,
+							&chan->conn_state)) {
+					control->final = 0;
+					l2cap_retransmit_all(chan, control);
+					l2cap_ertm_send(chan);
+				}
+			}
+
+			if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
+				l2cap_send_ack(chan);
+			break;
+		case L2CAP_TXSEQ_UNEXPECTED:
+			l2cap_pass_to_tx(chan, control);
+
+			/* Can't issue SREJ frames in the local busy state.
+			 * Drop this frame, it will be seen as missing
+			 * when local busy is exited.
+			 */
+			if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+				BT_DBG("Busy, discarding unexpected seq %d",
+				       control->txseq);
+				break;
+			}
+
+			/* There was a gap in the sequence, so an SREJ
+			 * must be sent for each missing frame.  The
+			 * current frame is stored for later use.
+			 */
+			skb_queue_tail(&chan->srej_q, skb);
+			skb_in_use = 1;
+			BT_DBG("Queued %p (queue len %d)", skb,
+			       skb_queue_len(&chan->srej_q));
+
+			clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+			l2cap_seq_list_clear(&chan->srej_list);
+			l2cap_send_srej(chan, control->txseq);
+
+			chan->rx_state = L2CAP_RX_STATE_SREJ_SENT;
+			break;
+		case L2CAP_TXSEQ_DUPLICATE:
+			l2cap_pass_to_tx(chan, control);
+			break;
+		case L2CAP_TXSEQ_INVALID_IGNORE:
+			break;
+		case L2CAP_TXSEQ_INVALID:
+		default:
+			l2cap_send_disconn_req(chan->conn, chan,
+					       ECONNRESET);
+			break;
+		}
+		break;
+	case L2CAP_EV_RECV_RR:
+		l2cap_pass_to_tx(chan, control);
+		if (control->final) {
+			clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+			if (!test_and_clear_bit(CONN_REJ_ACT,
+						&chan->conn_state)) {
+				control->final = 0;
+				l2cap_retransmit_all(chan, control);
+			}
+
+			l2cap_ertm_send(chan);
+		} else if (control->poll) {
+			l2cap_send_i_or_rr_or_rnr(chan);
+		} else {
+			if (test_and_clear_bit(CONN_REMOTE_BUSY,
+					       &chan->conn_state) &&
+			    chan->unacked_frames)
+				__set_retrans_timer(chan);
+
+			l2cap_ertm_send(chan);
+		}
+		break;
+	case L2CAP_EV_RECV_RNR:
+		set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+		l2cap_pass_to_tx(chan, control);
+		if (control && control->poll) {
+			set_bit(CONN_SEND_FBIT, &chan->conn_state);
+			l2cap_send_rr_or_rnr(chan, 0);
+		}
+		__clear_retrans_timer(chan);
+		l2cap_seq_list_clear(&chan->retrans_list);
+		break;
+	case L2CAP_EV_RECV_REJ:
+		l2cap_handle_rej(chan, control);
+		break;
+	case L2CAP_EV_RECV_SREJ:
+		l2cap_handle_srej(chan, control);
+		break;
+	default:
+		break;
+	}
+
+	if (skb && !skb_in_use) {
+		BT_DBG("Freeing %p", skb);
+		kfree_skb(skb);
+	}
+
+	return err;
+}
+
+static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
+				    struct l2cap_ctrl *control,
+				    struct sk_buff *skb, u8 event)
+{
+	int err = 0;
+	u16 txseq = control->txseq;
+	bool skb_in_use = 0;
+
+	BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+	       event);
+
+	switch (event) {
+	case L2CAP_EV_RECV_IFRAME:
+		switch (l2cap_classify_txseq(chan, txseq)) {
+		case L2CAP_TXSEQ_EXPECTED:
+			/* Keep frame for reassembly later */
+			l2cap_pass_to_tx(chan, control);
+			skb_queue_tail(&chan->srej_q, skb);
+			skb_in_use = 1;
+			BT_DBG("Queued %p (queue len %d)", skb,
+			       skb_queue_len(&chan->srej_q));
+
+			chan->expected_tx_seq = __next_seq(chan, txseq);
+			break;
+		case L2CAP_TXSEQ_EXPECTED_SREJ:
+			l2cap_seq_list_pop(&chan->srej_list);
+
+			l2cap_pass_to_tx(chan, control);
+			skb_queue_tail(&chan->srej_q, skb);
+			skb_in_use = 1;
+			BT_DBG("Queued %p (queue len %d)", skb,
+			       skb_queue_len(&chan->srej_q));
+
+			err = l2cap_rx_queued_iframes(chan);
+			if (err)
+				break;
+
+			break;
+		case L2CAP_TXSEQ_UNEXPECTED:
+			/* Got a frame that can't be reassembled yet.
+			 * Save it for later, and send SREJs to cover
+			 * the missing frames.
+			 */
+			skb_queue_tail(&chan->srej_q, skb);
+			skb_in_use = 1;
+			BT_DBG("Queued %p (queue len %d)", skb,
+			       skb_queue_len(&chan->srej_q));
+
+			l2cap_pass_to_tx(chan, control);
+			l2cap_send_srej(chan, control->txseq);
+			break;
+		case L2CAP_TXSEQ_UNEXPECTED_SREJ:
+			/* This frame was requested with an SREJ, but
+			 * some expected retransmitted frames are
+			 * missing.  Request retransmission of missing
+			 * SREJ'd frames.
+			 */
+			skb_queue_tail(&chan->srej_q, skb);
+			skb_in_use = 1;
+			BT_DBG("Queued %p (queue len %d)", skb,
+			       skb_queue_len(&chan->srej_q));
+
+			l2cap_pass_to_tx(chan, control);
+			l2cap_send_srej_list(chan, control->txseq);
+			break;
+		case L2CAP_TXSEQ_DUPLICATE_SREJ:
+			/* We've already queued this frame.  Drop this copy. */
+			l2cap_pass_to_tx(chan, control);
+			break;
+		case L2CAP_TXSEQ_DUPLICATE:
+			/* Expecting a later sequence number, so this frame
+			 * was already received.  Ignore it completely.
+			 */
+			break;
+		case L2CAP_TXSEQ_INVALID_IGNORE:
+			break;
+		case L2CAP_TXSEQ_INVALID:
+		default:
+			l2cap_send_disconn_req(chan->conn, chan,
+					       ECONNRESET);
+			break;
+		}
+		break;
+	case L2CAP_EV_RECV_RR:
+		l2cap_pass_to_tx(chan, control);
+		if (control->final) {
+			clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+			if (!test_and_clear_bit(CONN_REJ_ACT,
+						&chan->conn_state)) {
+				control->final = 0;
+				l2cap_retransmit_all(chan, control);
+			}
+
+			l2cap_ertm_send(chan);
+		} else if (control->poll) {
+			if (test_and_clear_bit(CONN_REMOTE_BUSY,
+					       &chan->conn_state) &&
+			    chan->unacked_frames) {
+				__set_retrans_timer(chan);
+			}
+
+			set_bit(CONN_SEND_FBIT, &chan->conn_state);
+			l2cap_send_srej_tail(chan);
+		} else {
+			if (test_and_clear_bit(CONN_REMOTE_BUSY,
+					       &chan->conn_state) &&
+			    chan->unacked_frames)
+				__set_retrans_timer(chan);
+
+			l2cap_send_ack(chan);
+		}
+		break;
+	case L2CAP_EV_RECV_RNR:
+		set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+		l2cap_pass_to_tx(chan, control);
+		if (control->poll) {
+			l2cap_send_srej_tail(chan);
+		} else {
+			struct l2cap_ctrl rr_control;
+			memset(&rr_control, 0, sizeof(rr_control));
+			rr_control.sframe = 1;
+			rr_control.super = L2CAP_SUPER_RR;
+			rr_control.reqseq = chan->buffer_seq;
+			l2cap_send_sframe(chan, &rr_control);
+		}
+
+		break;
+	case L2CAP_EV_RECV_REJ:
+		l2cap_handle_rej(chan, control);
+		break;
+	case L2CAP_EV_RECV_SREJ:
+		l2cap_handle_srej(chan, control);
+		break;
+	}
+
+	if (skb && !skb_in_use) {
+		BT_DBG("Freeing %p", skb);
+		kfree_skb(skb);
+	}
+
+	return err;
+}
+
+static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
+{
+	/* Make sure reqseq is for a packet that has been sent but not acked */
+	u16 unacked;
+
+	unacked = __seq_offset(chan, chan->next_tx_seq, chan->expected_ack_seq);
+	return __seq_offset(chan, chan->next_tx_seq, reqseq) <= unacked;
+}
+
 static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 		    struct sk_buff *skb, u8 event)
 {
-	/* Placeholder */
-	return -ENOTSUPP;
+	int err = 0;
+
+	BT_DBG("chan %p, control %p, skb %p, event %d, state %d", chan,
+	       control, skb, event, chan->rx_state);
+
+	if (__valid_reqseq(chan, control->reqseq)) {
+		switch (chan->rx_state) {
+		case L2CAP_RX_STATE_RECV:
+			err = l2cap_rx_state_recv(chan, control, skb, event);
+			break;
+		case L2CAP_RX_STATE_SREJ_SENT:
+			err = l2cap_rx_state_srej_sent(chan, control, skb,
+						       event);
+			break;
+		default:
+			/* shut it down */
+			break;
+		}
+	} else {
+		BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d",
+		       control->reqseq, chan->next_tx_seq,
+		       chan->expected_ack_seq);
+		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+	}
+
+	return err;
 }
 
 static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 13/25] Bluetooth: Add implementation for retransmitting all unacked frames
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (11 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 12/25] Bluetooth: Add the ERTM receive state machine Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 14/25] Bluetooth: Send SREJ frames when packets go missing Mat Martineau
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This adds l2cap_ertm_resend to retransmit frames based on the sequence
numbers in chan->retrans_list.  If the retransmit limit is reached for
any individual frame is reached, the connection is dropped.  skbs that
are cloned already are copied to avoid modifying shared data (this is
uncommon).  To retransmit all frames, l2cap_retransmit_all now builds
a list of all unacked sequence numbers and then calls
l2cap_ertm_resend.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |  106 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 105 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 86c4253..090d489 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1757,10 +1757,114 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 	return sent;
 }
 
+static void l2cap_ertm_resend(struct l2cap_chan *chan)
+{
+	struct l2cap_ctrl control;
+	struct sk_buff *skb;
+	struct sk_buff *tx_skb;
+	u16 seq;
+
+	BT_DBG("chan %p", chan);
+
+	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
+		return;
+
+	while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
+		seq = l2cap_seq_list_pop(&chan->retrans_list);
+
+		skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq);
+		if (!skb) {
+			BT_DBG("Error: Can't retransmit seq %d, frame missing",
+				seq);
+			continue;
+		}
+
+		bt_cb(skb)->control.retries++;
+		control = bt_cb(skb)->control;
+
+		if (chan->max_tx != 0 &&
+		    bt_cb(skb)->control.retries > chan->max_tx) {
+			BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
+			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+			l2cap_seq_list_clear(&chan->retrans_list);
+			break;
+		}
+
+		control.reqseq = chan->buffer_seq;
+		if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
+			control.final = 1;
+		else
+			control.final = 0;
+
+		if (skb_cloned(skb)) {
+			/* Cloned sk_buffs are read-only, so we need a
+			 * writeable copy
+			 */
+			tx_skb = skb_copy(skb, GFP_ATOMIC);
+		} else {
+			tx_skb = skb_clone(skb, GFP_ATOMIC);
+		}
+
+		if (!tx_skb) {
+			l2cap_seq_list_clear(&chan->retrans_list);
+			break;
+		}
+
+		/* Update skb contents */
+		if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
+			put_unaligned_le32(__pack_extended_control(&control),
+					   tx_skb->data + L2CAP_HDR_SIZE);
+		} else {
+			put_unaligned_le16(__pack_enhanced_control(&control),
+					   tx_skb->data + L2CAP_HDR_SIZE);
+		}
+
+		if (chan->fcs == L2CAP_FCS_CRC16) {
+			u16 fcs = crc16(0, (u8 *) tx_skb->data, tx_skb->len);
+			put_unaligned_le16(fcs, skb_put(tx_skb,
+							L2CAP_FCS_SIZE));
+		}
+
+		l2cap_do_send(chan, tx_skb);
+
+		BT_DBG("Resent txseq %d", control.txseq);
+
+		chan->last_acked_seq = chan->buffer_seq;
+	}
+}
+
 static void l2cap_retransmit_all(struct l2cap_chan *chan,
 				 struct l2cap_ctrl *control)
 {
-	/* Placeholder */
+	struct sk_buff *skb;
+
+	BT_DBG("chan %p, control %p", chan, control);
+
+	if (control->poll)
+		set_bit(CONN_SEND_FBIT, &chan->conn_state);
+
+	l2cap_seq_list_clear(&chan->retrans_list);
+
+	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
+		return;
+
+	if (chan->unacked_frames) {
+		skb_queue_walk(&chan->tx_q, skb) {
+			if (bt_cb(skb)->control.txseq == control->reqseq ||
+				skb == chan->tx_send_head)
+				break;
+		}
+
+		skb_queue_walk_from(&chan->tx_q, skb) {
+			if (skb == chan->tx_send_head)
+				break;
+
+			l2cap_seq_list_append(&chan->retrans_list,
+					      bt_cb(skb)->control.txseq);
+		}
+
+		l2cap_ertm_resend(chan);
+	}
 }
 
 static void l2cap_send_ack(struct l2cap_chan *chan)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 14/25] Bluetooth: Send SREJ frames when packets go missing
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (12 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 13/25] Bluetooth: Add implementation for retransmitting all unacked frames Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 15/25] Bluetooth: Reassemble all available data when retransmissions succeed Mat Martineau
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

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>
---
 net/bluetooth/l2cap_core.c |   56 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 3 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 090d489..4efab1b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2236,17 +2236,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)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 15/25] Bluetooth: Reassemble all available data when retransmissions succeed.
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (13 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 14/25] Bluetooth: Send SREJ frames when packets go missing Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 16/25] Bluetooth: Handle SREJ requests to resend unacked frames Mat Martineau
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

As retransmitted packets arrive, attempt to reassemble SDUs.  If all
requested retransmissions have been received, acknowledge them and
transition back to the RECV state.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4efab1b..7091f64 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4501,8 +4501,36 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
 
 static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
 {
-	/* Placeholder */
-	return 0;
+	int err = 0;
+	/* Pass sequential frames to l2cap_reassemble_sdu()
+	 * until a gap is encountered.
+	 */
+
+	BT_DBG("chan %p", chan);
+
+	while (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+		struct sk_buff *skb;
+		BT_DBG("Searching for skb with txseq %d (queue len %d)",
+		       chan->buffer_seq, skb_queue_len(&chan->srej_q));
+
+		skb = l2cap_ertm_seq_in_queue(&chan->srej_q, chan->buffer_seq);
+
+		if (!skb)
+			break;
+
+		skb_unlink(skb, &chan->srej_q);
+		chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
+		err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control);
+		if (err)
+			break;
+	}
+
+	if (skb_queue_empty(&chan->srej_q)) {
+		chan->rx_state = L2CAP_RX_STATE_RECV;
+		l2cap_send_ack(chan);
+	}
+
+	return err;
 }
 
 static void l2cap_handle_srej(struct l2cap_chan *chan,
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 16/25] Bluetooth: Handle SREJ requests to resend unacked frames
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (14 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 15/25] Bluetooth: Reassemble all available data when retransmissions succeed Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 17/25] Bluetooth: Handle incoming REJ frames Mat Martineau
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

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>
---
 net/bluetooth/l2cap_core.c |   70 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7091f64..cd32aa2 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1833,6 +1833,15 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
 	}
 }
 
+static void l2cap_retransmit(struct l2cap_chan *chan,
+			     struct l2cap_ctrl *control)
+{
+	BT_DBG("chan %p, control %p", chan, control);
+
+	l2cap_seq_list_append(&chan->retrans_list, control->reqseq);
+	l2cap_ertm_resend(chan);
+}
+
 static void l2cap_retransmit_all(struct l2cap_chan *chan,
 				 struct l2cap_ctrl *control)
 {
@@ -2529,6 +2538,13 @@ static void l2cap_pass_to_tx(struct l2cap_chan *chan,
 	l2cap_tx(chan, control, 0, L2CAP_EV_RECV_REQSEQ_AND_FBIT);
 }
 
+static void l2cap_pass_to_tx_fbit(struct l2cap_chan *chan,
+				  struct l2cap_ctrl *control)
+{
+	BT_DBG("chan %p, control %p", chan, control);
+	l2cap_tx(chan, control, 0, L2CAP_EV_RECV_FBIT);
+}
+
 /* Copy frame to all raw sockets on that connection */
 static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
 {
@@ -4536,7 +4552,59 @@ static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
 static void l2cap_handle_srej(struct l2cap_chan *chan,
 			      struct l2cap_ctrl *control)
 {
-	/* Placeholder */
+	struct sk_buff *skb;
+
+	BT_DBG("chan %p, control %p", chan, control);
+
+	if (control->reqseq == chan->next_tx_seq) {
+		BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
+		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		return;
+	}
+
+	skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
+
+	if (skb == NULL) {
+		BT_DBG("Seq %d not available for retransmission",
+		       control->reqseq);
+		return;
+	}
+
+	if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
+		BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
+		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		return;
+	}
+
+	clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+	if (control->poll) {
+		l2cap_pass_to_tx(chan, control);
+
+		set_bit(CONN_SEND_FBIT, &chan->conn_state);
+		l2cap_retransmit(chan, control);
+		l2cap_ertm_send(chan);
+
+		if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
+			set_bit(CONN_SREJ_ACT, &chan->conn_state);
+			chan->srej_save_reqseq = control->reqseq;
+		}
+	} else {
+		l2cap_pass_to_tx_fbit(chan, control);
+
+		if (control->final) {
+			if (chan->srej_save_reqseq != control->reqseq ||
+			    !test_and_clear_bit(CONN_SREJ_ACT,
+						&chan->conn_state))
+				l2cap_retransmit(chan, control);
+		} else {
+			l2cap_retransmit(chan, control);
+			if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
+				set_bit(CONN_SREJ_ACT, &chan->conn_state);
+				chan->srej_save_reqseq = control->reqseq;
+			}
+		}
+	}
 }
 
 static void l2cap_handle_rej(struct l2cap_chan *chan,
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 17/25] Bluetooth: Handle incoming REJ frames
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (15 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 16/25] Bluetooth: Handle SREJ requests to resend unacked frames Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 18/25] Bluetooth: Use new header structures in l2cap_send_rr_or_rnr Mat Martineau
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

REJ frames are sent by the remote device to request that all frames
after a given sequence number be retransmitted.  These are also an
implicit indication that the remote device is not in a busy state and
can receive new iframes.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cd32aa2..6e1200a 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4610,7 +4610,38 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
 static void l2cap_handle_rej(struct l2cap_chan *chan,
 			     struct l2cap_ctrl *control)
 {
-	/* Placeholder */
+	struct sk_buff *skb;
+
+	BT_DBG("chan %p, control %p", chan, control);
+
+	if (control->reqseq == chan->next_tx_seq) {
+		BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
+		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		return;
+	}
+
+	skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
+
+	if (chan->max_tx && skb &&
+	    bt_cb(skb)->control.retries >= chan->max_tx) {
+		BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
+		l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		return;
+	}
+
+	clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+	l2cap_pass_to_tx(chan, control);
+
+	if (control->final) {
+		if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
+			l2cap_retransmit_all(chan, control);
+	} else {
+		l2cap_retransmit_all(chan, control);
+		l2cap_ertm_send(chan);
+		if (chan->tx_state == L2CAP_TX_STATE_WAIT_F)
+			set_bit(CONN_REJ_ACT, &chan->conn_state);
+	}
 }
 
 static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 18/25] Bluetooth: Use new header structures in l2cap_send_rr_or_rnr
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (16 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 17/25] Bluetooth: Handle incoming REJ frames Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 19/25] Bluetooth: Check rules when setting retransmit or monitor timers Mat Martineau
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

struct l2cap_ctrl is now used, and the sframe is now sent directly
rather than depending on a separate call.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 6e1200a..0c55403 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -918,15 +918,23 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
 		l2cap_do_send(chan, skb);
 }
 
-static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
+static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll)
 {
-	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
-		control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
-		set_bit(CONN_RNR_SENT, &chan->conn_state);
-	} else
-		control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
+	struct l2cap_ctrl control;
 
-	control |= __set_reqseq(chan, chan->buffer_seq);
+	BT_DBG("chan %p, poll %d", chan, poll);
+
+	memset(&control, 0, sizeof(control));
+	control.sframe = 1;
+	control.poll = poll;
+
+	if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
+		control.super = L2CAP_SUPER_RNR;
+	else
+		control.super = L2CAP_SUPER_RR;
+
+	control.reqseq = chan->buffer_seq;
+	l2cap_send_sframe(chan, &control);
 }
 
 static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 19/25] Bluetooth: Check rules when setting retransmit or monitor timers
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (17 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 18/25] Bluetooth: Use new header structures in l2cap_send_rr_or_rnr Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 20/25] Bluetooth: Use the ERTM transmit state machine from timeout handlers Mat Martineau
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

The ERTM specification requires the retransmit timer to be cancelled
when the monitor timer is set.  The retransmit timer cannot be set
again while the monitor timer is pending.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 include/net/bluetooth/l2cap.h |    4 ----
 net/bluetooth/l2cap_core.c    |   22 ++++++++++++++++++++--
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 7d1da5a..117db8e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -706,11 +706,7 @@ static inline bool l2cap_clear_timer(struct l2cap_chan *chan,
 
 #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
 #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
-#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
-		msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
 #define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer)
-#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
-		msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
 #define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer)
 #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
 		msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO));
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0c55403..7bacb01 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -227,6 +227,24 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
 	release_sock(sk);
 }
 
+static void __set_retrans_timer(struct l2cap_chan *chan)
+{
+	if (!delayed_work_pending(&chan->monitor_timer) &&
+	    chan->retrans_timeout) {
+		l2cap_set_timer(chan, &chan->retrans_timer,
+				msecs_to_jiffies(chan->retrans_timeout));
+	}
+}
+
+static void __set_monitor_timer(struct l2cap_chan *chan)
+{
+	__clear_retrans_timer(chan);
+	if (chan->monitor_timeout) {
+		l2cap_set_timer(chan, &chan->monitor_timer,
+				msecs_to_jiffies(chan->monitor_timeout));
+	}
+}
+
 static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
 					       u16 seq)
 {
@@ -1616,7 +1634,7 @@ int __l2cap_wait_ack(struct sock *sk)
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
 	struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
-							monitor_timer.work);
+					       monitor_timer.work);
 
 	BT_DBG("chan %p", chan);
 
@@ -1640,7 +1658,7 @@ static void l2cap_monitor_timeout(struct work_struct *work)
 static void l2cap_retrans_timeout(struct work_struct *work)
 {
 	struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
-							retrans_timer.work);
+					       retrans_timer.work);
 
 	BT_DBG("chan %p", chan);
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 20/25] Bluetooth: Use the ERTM transmit state machine from timeout handlers
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (18 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 19/25] Bluetooth: Check rules when setting retransmit or monitor timers Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 21/25] Bluetooth: Simplify the ERTM ack timeout Mat Martineau
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Different states have different actions for retransmit and monitor
timeouts, so remove the logic for those actions from the timer handlers.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7bacb01..7f3d7cb 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1640,17 +1640,14 @@ static void l2cap_monitor_timeout(struct work_struct *work)
 
 	l2cap_chan_lock(chan);
 
-	if (chan->retry_count >= chan->remote_max_tx) {
-		l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
+	if (!chan->conn) {
 		l2cap_chan_unlock(chan);
 		l2cap_chan_put(chan);
 		return;
 	}
 
-	chan->retry_count++;
-	__set_monitor_timer(chan);
+	l2cap_tx(chan, 0, 0, L2CAP_EV_MONITOR_TO);
 
-	l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
 	l2cap_chan_unlock(chan);
 	l2cap_chan_put(chan);
 }
@@ -1664,13 +1661,13 @@ static void l2cap_retrans_timeout(struct work_struct *work)
 
 	l2cap_chan_lock(chan);
 
-	chan->retry_count = 1;
-	__set_monitor_timer(chan);
-
-	set_bit(CONN_WAIT_F, &chan->conn_state);
-
-	l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
+	if (!chan->conn) {
+		l2cap_chan_unlock(chan);
+		l2cap_chan_put(chan);
+		return;
+	}
 
+	l2cap_tx(chan, 0, 0, L2CAP_EV_RETRANS_TO);
 	l2cap_chan_unlock(chan);
 	l2cap_chan_put(chan);
 }
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 21/25] Bluetooth: Simplify the ERTM ack timeout
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (19 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 20/25] Bluetooth: Use the ERTM transmit state machine from timeout handlers Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 22/25] Bluetooth: Remove unneccesary inline Mat Martineau
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Since l2cap_send_ack can trigger extra actions like sending iframes,
don't call it.  Just send an RR or RNR frame if an ack needs sending.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7f3d7cb..afb7afc 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2758,16 +2758,20 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
 static void l2cap_ack_timeout(struct work_struct *work)
 {
 	struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
-							ack_timer.work);
+					       ack_timer.work);
+	u16 frames_to_ack;
 
 	BT_DBG("chan %p", chan);
 
 	l2cap_chan_lock(chan);
 
-	l2cap_send_ack(chan);
+	frames_to_ack = __seq_offset(chan, chan->buffer_seq,
+				     chan->last_acked_seq);
+
+	if (frames_to_ack)
+		l2cap_send_rr_or_rnr(chan, 0);
 
 	l2cap_chan_unlock(chan);
-
 	l2cap_chan_put(chan);
 }
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 22/25] Bluetooth: Remove unneccesary inline
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (20 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 21/25] Bluetooth: Simplify the ERTM ack timeout Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 23/25] Bluetooth: Set txwin values for streaming mode Mat Martineau
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Let the compiler decide if inlining is appropriate.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index afb7afc..5faddba 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4396,7 +4396,7 @@ static int l2cap_check_fcs(struct l2cap_chan *chan,  struct sk_buff *skb)
 	return 0;
 }
 
-static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
+static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
 {
 	struct l2cap_ctrl control;
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 23/25] Bluetooth: Set txwin values for streaming mode
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (21 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 22/25] Bluetooth: Remove unneccesary inline Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 24/25] Bluetooth: Remove unused ERTM control field macros Mat Martineau
  2012-05-18  3:53 ` [PATCH 25/25] Bluetooth: Enable ERTM by default Mat Martineau
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

The transmit window values must be configured for streaming mode, even
though streaming mode does not have a window.  This enables use of
extended headers when the transmit window socket option is set to 64
or larger.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5faddba..1a0f942 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2938,6 +2938,7 @@ done:
 		break;
 
 	case L2CAP_MODE_STREAMING:
+		l2cap_txwin_setup(chan);
 		rfc.mode            = L2CAP_MODE_STREAMING;
 		rfc.txwin_size      = 0;
 		rfc.max_transmit    = 0;
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 24/25] Bluetooth: Remove unused ERTM control field macros
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (22 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 23/25] Bluetooth: Set txwin values for streaming mode Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18  3:53 ` [PATCH 25/25] Bluetooth: Enable ERTM by default Mat Martineau
  24 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

Now that l2cap_ctrl is used to set up control fields, these macros are
not needed.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 include/net/bluetooth/l2cap.h |  168 -----------------------------------------
 1 file changed, 168 deletions(-)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 117db8e..7bc4019 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -725,174 +725,6 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
 	return (seq + 1) % (chan->tx_win_max + 1);
 }
 
-static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
-{
-	int sub;
-
-	sub = (ch->next_tx_seq - ch->expected_ack_seq) % 64;
-
-	if (sub < 0)
-		sub += 64;
-
-	return sub == ch->remote_tx_win;
-}
-
-static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >>
-						L2CAP_EXT_CTRL_REQSEQ_SHIFT;
-	else
-		return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
-}
-
-static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) &
-							L2CAP_EXT_CTRL_REQSEQ;
-	else
-		return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ;
-}
-
-static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >>
-						L2CAP_EXT_CTRL_TXSEQ_SHIFT;
-	else
-		return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
-}
-
-static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) &
-							L2CAP_EXT_CTRL_TXSEQ;
-	else
-		return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ;
-}
-
-static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE;
-	else
-		return ctrl & L2CAP_CTRL_FRAME_TYPE;
-}
-
-static inline __u32 __set_sframe(struct l2cap_chan *chan)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return L2CAP_EXT_CTRL_FRAME_TYPE;
-	else
-		return L2CAP_CTRL_FRAME_TYPE;
-}
-
-static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
-	else
-		return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
-}
-
-static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR;
-	else
-		return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR;
-}
-
-static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl)
-{
-	return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START;
-}
-
-static inline __u32 __get_sar_mask(struct l2cap_chan *chan)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return L2CAP_EXT_CTRL_SAR;
-	else
-		return L2CAP_CTRL_SAR;
-}
-
-static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >>
-						L2CAP_EXT_CTRL_SUPER_SHIFT;
-	else
-		return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
-}
-
-static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) &
-						L2CAP_EXT_CTRL_SUPERVISE;
-	else
-		return (super << L2CAP_CTRL_SUPER_SHIFT) &
-							L2CAP_CTRL_SUPERVISE;
-}
-
-static inline __u32 __set_ctrl_final(struct l2cap_chan *chan)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return L2CAP_EXT_CTRL_FINAL;
-	else
-		return L2CAP_CTRL_FINAL;
-}
-
-static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return ctrl & L2CAP_EXT_CTRL_FINAL;
-	else
-		return ctrl & L2CAP_CTRL_FINAL;
-}
-
-static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return L2CAP_EXT_CTRL_POLL;
-	else
-		return L2CAP_CTRL_POLL;
-}
-
-static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return ctrl & L2CAP_EXT_CTRL_POLL;
-	else
-		return ctrl & L2CAP_CTRL_POLL;
-}
-
-static inline __u32 __get_control(struct l2cap_chan *chan, void *p)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return get_unaligned_le32(p);
-	else
-		return get_unaligned_le16(p);
-}
-
-static inline void __put_control(struct l2cap_chan *chan, __u32 control,
-								void *p)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return put_unaligned_le32(control, p);
-	else
-		return put_unaligned_le16(control, p);
-}
-
-static inline __u8 __ctrl_size(struct l2cap_chan *chan)
-{
-	if (test_bit(FLAG_EXT_CTRL, &chan->flags))
-		return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE;
-	else
-		return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE;
-}
 
 extern bool disable_ertm;
 
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 25/25] Bluetooth: Enable ERTM by default
  2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
                   ` (23 preceding siblings ...)
  2012-05-18  3:53 ` [PATCH 24/25] Bluetooth: Remove unused ERTM control field macros Mat Martineau
@ 2012-05-18  3:53 ` Mat Martineau
  2012-05-18 20:56   ` Gustavo Padovan
  24 siblings, 1 reply; 29+ messages in thread
From: Mat Martineau @ 2012-05-18  3:53 UTC (permalink / raw)
  To: linux-bluetooth, gustavo, marcel, ulisses; +Cc: pkrystad

This enables the new receive and transmit state machines.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
---
 net/bluetooth/l2cap_core.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 1a0f942..0795614 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -57,7 +57,7 @@
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/smp.h>
 
-bool disable_ertm = 1;
+bool disable_ertm;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
 static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
-- 
1.7.10

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine.
  2012-05-18  3:53 ` [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine Mat Martineau
@ 2012-05-18 19:54   ` Gustavo Padovan
  2012-05-18 20:19     ` Mat Martineau
  0 siblings, 1 reply; 29+ messages in thread
From: Gustavo Padovan @ 2012-05-18 19:54 UTC (permalink / raw)
  To: Mat Martineau; +Cc: linux-bluetooth, marcel, ulisses, pkrystad

Hi Mat,

* Mat Martineau <mathewm@codeaurora.org> [2012-05-17 20:53:32 -0700]:

> This implements a top-level transmit state machine with handlers for
> the two ERTM states defined in the specification: XMIT and WAIT_F.
> 
> The state machine accepts an event and, optionally, a list of skbs to
> transmit.  In addition to data transmission, the local busy state can
> be modified, acks are processed, and monitor and retransmit timeouts
> are handled.  This mirrors the structure of the state tables in the
> spec.
> 
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> ---
>  net/bluetooth/l2cap_core.c |  254 ++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 246 insertions(+), 8 deletions(-)
> 
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 6557367..a131403 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -73,6 +73,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
>  static void l2cap_send_disconn_req(struct l2cap_conn *conn,
>  				   struct l2cap_chan *chan, int err);
>  
> +static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
> +		    struct sk_buff_head *skbs, u8 event);
> +
>  /* ---- L2CAP channels ---- */
>  
>  static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
> @@ -224,6 +227,19 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
>  	release_sock(sk);
>  }
>  
> +static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
> +					       u16 seq)
> +{
> +	struct sk_buff *skb;
> +
> +	skb_queue_walk(head, skb) {
> +		if (bt_cb(skb)->control.txseq == seq)
> +			return skb;
> +	}
> +
> +	return NULL;
> +}
> +
>  /* ---- L2CAP sequence number lists ---- */
>  
>  /* For ERTM, ordered lists of sequence numbers must be tracked for
> @@ -2117,16 +2133,15 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>  		if (err)
>  			break;
>  
> -		if (chan->mode == L2CAP_MODE_ERTM && chan->tx_send_head == NULL)
> -			chan->tx_send_head = seg_queue.next;
> -		skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
> -
> -		if (chan->mode == L2CAP_MODE_ERTM)
> -			err = l2cap_ertm_send(chan);
> -		else
> +		if (chan->mode == L2CAP_MODE_ERTM) {
> +			err = l2cap_tx(chan, 0, &seg_queue,
> +				       L2CAP_EV_DATA_REQUEST);
> +		} else {
> +			skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
>  			l2cap_streaming_send(chan);
> +		}
>  
> -		if (err >= 0)
> +		if (!err)
>  			err = len;
>  
>  		/* If the skbs were not queued for sending, they'll still be in
> @@ -2143,6 +2158,229 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>  	return err;
>  }
>  
> +static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
> +{
> +	struct sk_buff *acked_skb;
> +	u16 ackseq;
> +
> +	BT_DBG("chan %p, reqseq %d", chan, reqseq);
> +
> +	if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq)
> +		return;
> +
> +	BT_DBG("expected_ack_seq %d, unacked_frames %d",
> +	       chan->expected_ack_seq, chan->unacked_frames);
> +
> +	for (ackseq = chan->expected_ack_seq; ackseq != reqseq;
> +	     ackseq = __next_seq(chan, ackseq)) {
> +
> +		acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq);
> +		if (acked_skb) {
> +			skb_unlink(acked_skb, &chan->tx_q);
> +			kfree_skb(acked_skb);
> +			chan->unacked_frames--;
> +		}
> +	}
> +
> +	chan->expected_ack_seq = reqseq;
> +
> +	if (chan->unacked_frames == 0)
> +		__clear_retrans_timer(chan);
> +
> +	BT_DBG("unacked_frames %d", (int) chan->unacked_frames);
> +}
> +
> +static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan)
> +{
> +	BT_DBG("chan %p", chan);
> +
> +	chan->expected_tx_seq = chan->buffer_seq;
> +	l2cap_seq_list_clear(&chan->srej_list);
> +	skb_queue_purge(&chan->srej_q);
> +	chan->rx_state = L2CAP_RX_STATE_RECV;
> +}
> +
> +static int l2cap_tx_state_xmit(struct l2cap_chan *chan,
> +			       struct l2cap_ctrl *control,
> +			       struct sk_buff_head *skbs, u8 event)
> +{
> +	int err = 0;
> +
> +	BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
> +	       event);
> +
> +	switch (event) {
> +	case L2CAP_EV_DATA_REQUEST:
> +		if (chan->tx_send_head == NULL)
> +			chan->tx_send_head = skb_peek(skbs);
> +
> +		skb_queue_splice_tail_init(skbs, &chan->tx_q);
> +		l2cap_ertm_send(chan);
> +		break;
> +	case L2CAP_EV_LOCAL_BUSY_DETECTED:
> +		BT_DBG("Enter LOCAL_BUSY");
> +		set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
> +
> +		if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
> +			/* The SREJ_SENT state must be aborted if we are to
> +			 * enter the LOCAL_BUSY state.
> +			 */
> +			l2cap_abort_rx_srej_sent(chan);
> +		}
> +
> +		l2cap_send_ack(chan);
> +
> +		break;
> +	case L2CAP_EV_LOCAL_BUSY_CLEAR:
> +		BT_DBG("Exit LOCAL_BUSY");
> +		clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
> +
> +		if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
> +			struct l2cap_ctrl local_control;
> +
> +			memset(&local_control, 0, sizeof(local_control));
> +			local_control.sframe = 1;
> +			local_control.super = L2CAP_SUPER_RR;
> +			local_control.poll = 1;
> +			local_control.reqseq = chan->buffer_seq;

You are not using local_control anywhere? Should it be removed, or other patch
in this patchset uses it?

	Gustavo

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine.
  2012-05-18 19:54   ` Gustavo Padovan
@ 2012-05-18 20:19     ` Mat Martineau
  0 siblings, 0 replies; 29+ messages in thread
From: Mat Martineau @ 2012-05-18 20:19 UTC (permalink / raw)
  To: Gustavo Padovan; +Cc: linux-bluetooth, marcel, ulisses, pkrystad


On Fri, 18 May 2012, Gustavo Padovan wrote:

> Hi Mat,
>
> * Mat Martineau <mathewm@codeaurora.org> [2012-05-17 20:53:32 -0700]:
>
>> This implements a top-level transmit state machine with handlers for
>> the two ERTM states defined in the specification: XMIT and WAIT_F.
>>
>> The state machine accepts an event and, optionally, a list of skbs to
>> transmit.  In addition to data transmission, the local busy state can
>> be modified, acks are processed, and monitor and retransmit timeouts
>> are handled.  This mirrors the structure of the state tables in the
>> spec.
>>
>> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
>> ---
>>  net/bluetooth/l2cap_core.c |  254 ++++++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 246 insertions(+), 8 deletions(-)
>>
>> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
>> index 6557367..a131403 100644
>> --- a/net/bluetooth/l2cap_core.c
>> +++ b/net/bluetooth/l2cap_core.c
>> @@ -73,6 +73,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
>>  static void l2cap_send_disconn_req(struct l2cap_conn *conn,
>>  				   struct l2cap_chan *chan, int err);
>>
>> +static int l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
>> +		    struct sk_buff_head *skbs, u8 event);
>> +
>>  /* ---- L2CAP channels ---- */
>>
>>  static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
>> @@ -224,6 +227,19 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
>>  	release_sock(sk);
>>  }
>>
>> +static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
>> +					       u16 seq)
>> +{
>> +	struct sk_buff *skb;
>> +
>> +	skb_queue_walk(head, skb) {
>> +		if (bt_cb(skb)->control.txseq == seq)
>> +			return skb;
>> +	}
>> +
>> +	return NULL;
>> +}
>> +
>>  /* ---- L2CAP sequence number lists ---- */
>>
>>  /* For ERTM, ordered lists of sequence numbers must be tracked for
>> @@ -2117,16 +2133,15 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>>  		if (err)
>>  			break;
>>
>> -		if (chan->mode == L2CAP_MODE_ERTM && chan->tx_send_head == NULL)
>> -			chan->tx_send_head = seg_queue.next;
>> -		skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
>> -
>> -		if (chan->mode == L2CAP_MODE_ERTM)
>> -			err = l2cap_ertm_send(chan);
>> -		else
>> +		if (chan->mode == L2CAP_MODE_ERTM) {
>> +			err = l2cap_tx(chan, 0, &seg_queue,
>> +				       L2CAP_EV_DATA_REQUEST);
>> +		} else {
>> +			skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
>>  			l2cap_streaming_send(chan);
>> +		}
>>
>> -		if (err >= 0)
>> +		if (!err)
>>  			err = len;
>>
>>  		/* If the skbs were not queued for sending, they'll still be in
>> @@ -2143,6 +2158,229 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
>>  	return err;
>>  }
>>
>> +static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
>> +{
>> +	struct sk_buff *acked_skb;
>> +	u16 ackseq;
>> +
>> +	BT_DBG("chan %p, reqseq %d", chan, reqseq);
>> +
>> +	if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq)
>> +		return;
>> +
>> +	BT_DBG("expected_ack_seq %d, unacked_frames %d",
>> +	       chan->expected_ack_seq, chan->unacked_frames);
>> +
>> +	for (ackseq = chan->expected_ack_seq; ackseq != reqseq;
>> +	     ackseq = __next_seq(chan, ackseq)) {
>> +
>> +		acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq);
>> +		if (acked_skb) {
>> +			skb_unlink(acked_skb, &chan->tx_q);
>> +			kfree_skb(acked_skb);
>> +			chan->unacked_frames--;
>> +		}
>> +	}
>> +
>> +	chan->expected_ack_seq = reqseq;
>> +
>> +	if (chan->unacked_frames == 0)
>> +		__clear_retrans_timer(chan);
>> +
>> +	BT_DBG("unacked_frames %d", (int) chan->unacked_frames);
>> +}
>> +
>> +static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan)
>> +{
>> +	BT_DBG("chan %p", chan);
>> +
>> +	chan->expected_tx_seq = chan->buffer_seq;
>> +	l2cap_seq_list_clear(&chan->srej_list);
>> +	skb_queue_purge(&chan->srej_q);
>> +	chan->rx_state = L2CAP_RX_STATE_RECV;
>> +}
>> +
>> +static int l2cap_tx_state_xmit(struct l2cap_chan *chan,
>> +			       struct l2cap_ctrl *control,
>> +			       struct sk_buff_head *skbs, u8 event)
>> +{
>> +	int err = 0;
>> +
>> +	BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
>> +	       event);
>> +
>> +	switch (event) {
>> +	case L2CAP_EV_DATA_REQUEST:
>> +		if (chan->tx_send_head == NULL)
>> +			chan->tx_send_head = skb_peek(skbs);
>> +
>> +		skb_queue_splice_tail_init(skbs, &chan->tx_q);
>> +		l2cap_ertm_send(chan);
>> +		break;
>> +	case L2CAP_EV_LOCAL_BUSY_DETECTED:
>> +		BT_DBG("Enter LOCAL_BUSY");
>> +		set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
>> +
>> +		if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
>> +			/* The SREJ_SENT state must be aborted if we are to
>> +			 * enter the LOCAL_BUSY state.
>> +			 */
>> +			l2cap_abort_rx_srej_sent(chan);
>> +		}
>> +
>> +		l2cap_send_ack(chan);
>> +
>> +		break;
>> +	case L2CAP_EV_LOCAL_BUSY_CLEAR:
>> +		BT_DBG("Exit LOCAL_BUSY");
>> +		clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
>> +
>> +		if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
>> +			struct l2cap_ctrl local_control;
>> +
>> +			memset(&local_control, 0, sizeof(local_control));
>> +			local_control.sframe = 1;
>> +			local_control.super = L2CAP_SUPER_RR;
>> +			local_control.poll = 1;
>> +			local_control.reqseq = chan->buffer_seq;
>
> You are not using local_control anywhere? Should it be removed, or other patch
> in this patchset uses it?

Patch 5 uses it.  The next line (call to l2cap_send_sframe) is where 
it is used, but l2cap_send_sframe hasn't been modified to take an 
l2cap_ctrl pointer yet.

--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 25/25] Bluetooth: Enable ERTM by default
  2012-05-18  3:53 ` [PATCH 25/25] Bluetooth: Enable ERTM by default Mat Martineau
@ 2012-05-18 20:56   ` Gustavo Padovan
  0 siblings, 0 replies; 29+ messages in thread
From: Gustavo Padovan @ 2012-05-18 20:56 UTC (permalink / raw)
  To: Mat Martineau; +Cc: linux-bluetooth, marcel, ulisses, pkrystad

Hi Mat,

* Mat Martineau <mathewm@codeaurora.org> [2012-05-17 20:53:55 -0700]:

> This enables the new receive and transmit state machines.
> 
> Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
> ---
>  net/bluetooth/l2cap_core.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

The whole series have been applied to bluetooth-next. Thanks.

	Gustavo

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2012-05-18 20:56 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-18  3:53 [PATCH 00/25] ERTM state machine changes, part 3 Mat Martineau
2012-05-18  3:53 ` [PATCH 01/25] Bluetooth: Change default state of ERTM disable flag Mat Martineau
2012-05-18  3:53 ` [PATCH 02/25] Bluetooth: Add a new L2CAP ERTM transmit state machine Mat Martineau
2012-05-18 19:54   ` Gustavo Padovan
2012-05-18 20:19     ` Mat Martineau
2012-05-18  3:53 ` [PATCH 03/25] Bluetooth: Refactor l2cap_streaming_send Mat Martineau
2012-05-18  3:53 ` [PATCH 04/25] Bluetooth: Refactor l2cap_ertm_send Mat Martineau
2012-05-18  3:53 ` [PATCH 05/25] Bluetooth: Refactor l2cap_send_sframe Mat Martineau
2012-05-18  3:53 ` [PATCH 06/25] Bluetooth: Consolidate common receive code for ERTM and streaming mode Mat Martineau
2012-05-18  3:53 ` [PATCH 07/25] Bluetooth: Add streaming mode receive and incoming packet classifier Mat Martineau
2012-05-18  3:53 ` [PATCH 08/25] Bluetooth: Remove receive code that has been superceded Mat Martineau
2012-05-18  3:53 ` [PATCH 09/25] Bluetooth: Refactor l2cap_send_ack Mat Martineau
2012-05-18  3:53 ` [PATCH 10/25] Bluetooth: Use the transmit state machine for busy state changes Mat Martineau
2012-05-18  3:53 ` [PATCH 11/25] Bluetooth: Update l2cap_send_i_or_rr_or_rnr to fit the spec better Mat Martineau
2012-05-18  3:53 ` [PATCH 12/25] Bluetooth: Add the ERTM receive state machine Mat Martineau
2012-05-18  3:53 ` [PATCH 13/25] Bluetooth: Add implementation for retransmitting all unacked frames Mat Martineau
2012-05-18  3:53 ` [PATCH 14/25] Bluetooth: Send SREJ frames when packets go missing Mat Martineau
2012-05-18  3:53 ` [PATCH 15/25] Bluetooth: Reassemble all available data when retransmissions succeed Mat Martineau
2012-05-18  3:53 ` [PATCH 16/25] Bluetooth: Handle SREJ requests to resend unacked frames Mat Martineau
2012-05-18  3:53 ` [PATCH 17/25] Bluetooth: Handle incoming REJ frames Mat Martineau
2012-05-18  3:53 ` [PATCH 18/25] Bluetooth: Use new header structures in l2cap_send_rr_or_rnr Mat Martineau
2012-05-18  3:53 ` [PATCH 19/25] Bluetooth: Check rules when setting retransmit or monitor timers Mat Martineau
2012-05-18  3:53 ` [PATCH 20/25] Bluetooth: Use the ERTM transmit state machine from timeout handlers Mat Martineau
2012-05-18  3:53 ` [PATCH 21/25] Bluetooth: Simplify the ERTM ack timeout Mat Martineau
2012-05-18  3:53 ` [PATCH 22/25] Bluetooth: Remove unneccesary inline Mat Martineau
2012-05-18  3:53 ` [PATCH 23/25] Bluetooth: Set txwin values for streaming mode Mat Martineau
2012-05-18  3:53 ` [PATCH 24/25] Bluetooth: Remove unused ERTM control field macros Mat Martineau
2012-05-18  3:53 ` [PATCH 25/25] Bluetooth: Enable ERTM by default Mat Martineau
2012-05-18 20:56   ` Gustavo Padovan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.