Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCHv2 01/19] Bluetooth: Add new l2cap_chan struct members for high speed channels
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel, Andrei Emeltchenko
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

An L2CAP channel using high speed continues to be associated with a
BR/EDR l2cap_conn, while also tracking an additional hci_conn
(representing a physical link on a high speed controller) and hci_chan
(representing a logical link).  There may only be one physical link
between two high speed controllers.  Each physical link may contain
several logical links, with each logical link representing a channel
with specific quality of service.

During a channel move, the destination channel id, current move state,
and role (initiator vs. responder) are tracked and used by the channel
move state machine.  The ident value associated with a move request
must also be stored in order to use it in later move responses.

The active channel is stored in local_amp_id.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++++++++++
 net/bluetooth/l2cap_core.c    |  5 +++++
 2 files changed, 34 insertions(+)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 6e23afd..6d3615e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -434,6 +434,8 @@ struct l2cap_chan {
 	struct sock *sk;
 
 	struct l2cap_conn	*conn;
+	struct hci_conn		*hs_hcon;
+	struct hci_chan		*hs_hchan;
 	struct kref	kref;
 
 	__u8		state;
@@ -477,6 +479,11 @@ struct l2cap_chan {
 	unsigned long	conn_state;
 	unsigned long	flags;
 
+	__u8		local_amp_id;
+	__u8		move_id;
+	__u8		move_state;
+	__u8		move_role;
+
 	__u16		next_tx_seq;
 	__u16		expected_ack_seq;
 	__u16		expected_tx_seq;
@@ -644,6 +651,9 @@ enum {
 enum {
 	L2CAP_RX_STATE_RECV,
 	L2CAP_RX_STATE_SREJ_SENT,
+	L2CAP_RX_STATE_MOVE,
+	L2CAP_RX_STATE_WAIT_P,
+	L2CAP_RX_STATE_WAIT_F,
 };
 
 enum {
@@ -674,6 +684,25 @@ enum {
 	L2CAP_EV_RECV_FRAME,
 };
 
+enum {
+	L2CAP_MOVE_ROLE_NONE,
+	L2CAP_MOVE_ROLE_INITIATOR,
+	L2CAP_MOVE_ROLE_RESPONDER,
+};
+
+enum {
+	L2CAP_MOVE_STABLE,
+	L2CAP_MOVE_WAIT_REQ,
+	L2CAP_MOVE_WAIT_RSP,
+	L2CAP_MOVE_WAIT_RSP_SUCCESS,
+	L2CAP_MOVE_WAIT_CONFIRM,
+	L2CAP_MOVE_WAIT_CONFIRM_RSP,
+	L2CAP_MOVE_WAIT_LOGICAL_COMP,
+	L2CAP_MOVE_WAIT_LOGICAL_CFM,
+	L2CAP_MOVE_WAIT_LOCAL_BUSY,
+	L2CAP_MOVE_WAIT_PREPARE,
+};
+
 void l2cap_chan_hold(struct l2cap_chan *c);
 void l2cap_chan_put(struct l2cap_chan *c);
 
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index f873619..f8d78f5 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2788,6 +2788,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
 
 	skb_queue_head_init(&chan->tx_q);
 
+	chan->local_amp_id = 0;
+	chan->move_id = 0;
+	chan->move_state = L2CAP_MOVE_STABLE;
+	chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
 	if (chan->mode != L2CAP_MODE_ERTM)
 		return 0;
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 02/19] Bluetooth: Add L2CAP create channel request handling
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel, Andrei Emeltchenko
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

The L2CAP create channel request is very similar to an L2CAP connect
request, but it has an additional parameter for the controller ID.  If
the controller id is 0, the channel is set up on the BR/EDR controller
(just like a connect request).  Using a valid high speed controller ID
will cause the channel to be initially created on that high speed
controller.  While the L2CAP data will be initially routed over the
AMP controller, the L2CAP fixed signaling channel only uses BR/EDR.

When a create channel request is received for a high speed controller,
a pending response is always sent first.  After the high speed
physical and logical links are complete a success response will be
sent.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 62 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 47 insertions(+), 15 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index f8d78f5..73ce337 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3400,8 +3400,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
 	return 0;
 }
 
-static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
-			  u8 *data, u8 rsp_code, u8 amp_id)
+static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
+					struct l2cap_cmd_hdr *cmd,
+					u8 *data, u8 rsp_code, u8 amp_id)
 {
 	struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
 	struct l2cap_conn_rsp rsp;
@@ -3452,6 +3453,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
 	bacpy(&bt_sk(sk)->dst, conn->dst);
 	chan->psm  = psm;
 	chan->dcid = scid;
+	chan->local_amp_id = amp_id;
 
 	__l2cap_chan_add(conn, chan);
 
@@ -3469,8 +3471,16 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
 				status = L2CAP_CS_AUTHOR_PEND;
 				chan->ops->defer(chan);
 			} else {
-				__l2cap_state_change(chan, BT_CONFIG);
-				result = L2CAP_CR_SUCCESS;
+				/* Force pending result for AMP controllers.
+				 * The connection will succeed after the
+				 * physical link is up. */
+				if (amp_id) {
+					__l2cap_state_change(chan, BT_CONNECT2);
+					result = L2CAP_CR_PEND;
+				} else {
+					__l2cap_state_change(chan, BT_CONFIG);
+					result = L2CAP_CR_SUCCESS;
+				}
 				status = L2CAP_CS_NO_INFO;
 			}
 		} else {
@@ -3516,6 +3526,8 @@ sendresp:
 			       l2cap_build_conf_req(chan, buf), buf);
 		chan->num_conf_req++;
 	}
+
+	return chan;
 }
 
 static int l2cap_connect_req(struct l2cap_conn *conn,
@@ -4028,12 +4040,12 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
 	return 0;
 }
 
-static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
-					   struct l2cap_cmd_hdr *cmd,
-					   u16 cmd_len, void *data)
+static int l2cap_create_channel_req(struct l2cap_conn *conn,
+				    struct l2cap_cmd_hdr *cmd,
+				    u16 cmd_len, void *data)
 {
 	struct l2cap_create_chan_req *req = data;
-	struct l2cap_create_chan_rsp rsp;
+	struct l2cap_chan *chan;
 	u16 psm, scid;
 
 	if (cmd_len != sizeof(*req))
@@ -4047,14 +4059,34 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
 
 	BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
 
-	/* Placeholder: Always reject */
-	rsp.dcid = 0;
-	rsp.scid = cpu_to_le16(scid);
-	rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
-	rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+	if (req->amp_id) {
+		struct hci_dev *hdev;
 
-	l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
-		       sizeof(rsp), &rsp);
+		/* Validate AMP controller id */
+		hdev = hci_dev_get(req->amp_id);
+		if (!hdev || hdev->dev_type != HCI_AMP ||
+		    !test_bit(HCI_UP, &hdev->flags)) {
+			struct l2cap_create_chan_rsp rsp;
+
+			rsp.dcid = 0;
+			rsp.scid = cpu_to_le16(scid);
+			rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
+			rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+
+			l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
+				       sizeof(rsp), &rsp);
+
+			if (hdev)
+				hci_dev_put(hdev);
+
+			return 0;
+		}
+
+		hci_dev_put(hdev);
+	}
+
+	chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+			     req->amp_id);
 
 	return 0;
 }
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 03/19] Bluetooth: Remove unnecessary intermediate function
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Resolves a conflict resolution issue in "Bluetooth: Fix L2CAP coding
style".

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 73ce337..ec2b4d9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3537,7 +3537,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
 	return 0;
 }
 
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn,
+static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
 				    struct l2cap_cmd_hdr *cmd, u8 *data)
 {
 	struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
@@ -4091,15 +4091,6 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
 	return 0;
 }
 
-static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
-					   struct l2cap_cmd_hdr *cmd,
-					   void *data)
-{
-	BT_DBG("conn %p", conn);
-
-	return l2cap_connect_rsp(conn, cmd, data);
-}
-
 static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
 				     u16 icid, u16 result)
 {
@@ -4306,7 +4297,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
 
 	case L2CAP_CONN_RSP:
 	case L2CAP_CREATE_CHAN_RSP:
-		err = l2cap_connect_rsp(conn, cmd, data);
+		err = l2cap_connect_create_rsp(conn, cmd, data);
 		break;
 
 	case L2CAP_CONF_REQ:
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 04/19] Bluetooth: Lookup channel structure based on DCID
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Processing a move channel request involves getting the channel
structure using the destination channel ID.  Previous code could only
look up using the source channel ID.

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

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ec2b4d9..b5b849b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -100,6 +100,22 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
 	return c;
 }
 
+/* Find channel with given DCID.
+ * Returns locked channel. */
+static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
+						 u16 cid)
+{
+	struct l2cap_chan *c;
+
+	mutex_lock(&conn->chan_lock);
+	c = __l2cap_get_chan_by_dcid(conn, cid);
+	if (c)
+		l2cap_chan_lock(c);
+	mutex_unlock(&conn->chan_lock);
+
+	return c;
+}
+
 static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
 						    u8 ident)
 {
@@ -4139,6 +4155,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 					 u16 cmd_len, void *data)
 {
 	struct l2cap_move_chan_req *req = data;
+	struct l2cap_chan *chan;
 	u16 icid = 0;
 	u16 result = L2CAP_MR_NOT_ALLOWED;
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 05/19] Bluetooth: Channel move request handling
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

On receipt of a channel move request, the request must be validated
based on the L2CAP mode, connection state, and controller
capabilities.  ERTM channels must have their state machines cleared
and transmission paused while the channel move takes place.

If the channel is being moved to an AMP controller then
an AMP physical link must be prepared.  Moving the channel back to
BR/EDR proceeds immediately.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 108 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b5b849b..2fb0567 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -734,6 +734,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
 	hci_send_acl(conn->hchan, skb, flags);
 }
 
+static bool __chan_is_moving(struct l2cap_chan *chan)
+{
+	return chan->move_state != L2CAP_MOVE_STABLE &&
+	       chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
+}
+
 static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
 {
 	struct hci_conn *hcon = chan->conn->hcon;
@@ -995,6 +1001,41 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
 	l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
 }
 
+static void l2cap_move_setup(struct l2cap_chan *chan)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("chan %p", chan);
+
+	if (chan->mode != L2CAP_MODE_ERTM)
+		return;
+
+	__clear_retrans_timer(chan);
+	__clear_monitor_timer(chan);
+	__clear_ack_timer(chan);
+
+	chan->retry_count = 0;
+	skb_queue_walk(&chan->tx_q, skb) {
+		if (bt_cb(skb)->control.retries)
+			bt_cb(skb)->control.retries = 1;
+		else
+			break;
+	}
+
+	chan->expected_tx_seq = chan->buffer_seq;
+
+	clear_bit(CONN_REJ_ACT, &chan->conn_state);
+	clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+	l2cap_seq_list_clear(&chan->retrans_list);
+	l2cap_seq_list_clear(&chan->srej_list);
+	skb_queue_purge(&chan->srej_q);
+
+	chan->tx_state = L2CAP_TX_STATE_XMIT;
+	chan->rx_state = L2CAP_RX_STATE_MOVE;
+
+	set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+}
+
 static void l2cap_chan_ready(struct l2cap_chan *chan)
 {
 	/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -4169,9 +4210,74 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 	if (!enable_hs)
 		return -EINVAL;
 
-	/* Placeholder: Always refuse */
+	chan = l2cap_get_chan_by_dcid(conn, icid);
+
+	if (!chan || chan->scid < L2CAP_CID_DYN_START ||
+	    chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
+	    (chan->mode != L2CAP_MODE_ERTM &&
+	     chan->mode != L2CAP_MODE_STREAMING)) {
+		result = L2CAP_MR_NOT_ALLOWED;
+		goto send_move_response;
+	}
+
+	if (chan->local_amp_id == req->dest_amp_id) {
+		result = L2CAP_MR_SAME_ID;
+		goto send_move_response;
+	}
+
+	if (req->dest_amp_id) {
+		struct hci_dev *hdev;
+		hdev = hci_dev_get(req->dest_amp_id);
+		if (!hdev || hdev->dev_type != HCI_AMP ||
+		    !test_bit(HCI_UP, &hdev->flags)) {
+			if (hdev)
+				hci_dev_put(hdev);
+
+			result = L2CAP_MR_BAD_ID;
+			goto send_move_response;
+		}
+		hci_dev_put(hdev);
+	}
+
+	/* Detect a move collision.  Only send a collision response
+	 * if this side has "lost", otherwise proceed with the move.
+	 * The winner has the larger bd_addr.
+	 */
+	if ((__chan_is_moving(chan) ||
+	     chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
+	    bacmp(conn->src, conn->dst) > 0) {
+		result = L2CAP_MR_COLLISION;
+		goto send_move_response;
+	}
+
+	chan->ident = cmd->ident;
+	chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+	l2cap_move_setup(chan);
+	chan->move_id = req->dest_amp_id;
+	icid = chan->dcid;
+
+	if (req->dest_amp_id == 0) {
+		/* Moving to BR/EDR */
+		if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+			chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+			result = L2CAP_MR_PEND;
+		} else {
+			chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+			result = L2CAP_MR_SUCCESS;
+		}
+	} else {
+		chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+		/* Placeholder - uncomment when amp functions are available */
+		/*amp_accept_physical(chan, req->dest_amp_id);*/
+		result = L2CAP_MR_PEND;
+	}
+
+send_move_response:
 	l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
 
+	if (chan)
+		l2cap_chan_unlock(chan);
+
 	return 0;
 }
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 06/19] Bluetooth: Add new ERTM receive states for channel move
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Two new states are required to implement channel moves with the ERTM
receive state machine.

The "WAIT_P" state is used by a move responder to wait for a "poll"
flag after a move is completed (success or failure).  "WAIT_F" is
similarly used by a move initiator to wait for a "final" flag when the
move is completing.  In either state, the reqseq value in the
poll/final frame tells the state machine exactly which frame should be
expected next.

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

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2fb0567..9663292 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4707,6 +4707,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
 	return err;
 }
 
+static int l2cap_resegment(struct l2cap_chan *chan)
+{
+	/* Placeholder */
+	return 0;
+}
+
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
 {
 	u8 event;
@@ -5212,6 +5218,98 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
 	return err;
 }
 
+static int l2cap_finish_move(struct l2cap_chan *chan)
+{
+	BT_DBG("chan %p", chan);
+
+	chan->move_role = L2CAP_MOVE_ROLE_NONE;
+	chan->rx_state = L2CAP_RX_STATE_RECV;
+
+	if (chan->hs_hcon)
+		chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+	else
+		chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+	return l2cap_resegment(chan);
+}
+
+static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
+				 struct l2cap_ctrl *control,
+				 struct sk_buff *skb, u8 event)
+{
+	int err;
+
+	BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+	       event);
+
+	if (!control->poll)
+		return -EPROTO;
+
+	l2cap_process_reqseq(chan, control->reqseq);
+
+	if (!skb_queue_empty(&chan->tx_q))
+		chan->tx_send_head = skb_peek(&chan->tx_q);
+	else
+		chan->tx_send_head = NULL;
+
+	/* Rewind next_tx_seq to the point expected
+	 * by the receiver.
+	 */
+	chan->next_tx_seq = control->reqseq;
+	chan->unacked_frames = 0;
+
+	err = l2cap_finish_move(chan);
+	if (err)
+		return err;
+
+	set_bit(CONN_SEND_FBIT, &chan->conn_state);
+	l2cap_send_i_or_rr_or_rnr(chan);
+
+	if (event == L2CAP_EV_RECV_IFRAME)
+		return -EPROTO;
+
+	return l2cap_rx_state_recv(chan, control, NULL, event);
+}
+
+static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
+				 struct l2cap_ctrl *control,
+				 struct sk_buff *skb, u8 event)
+{
+	int err;
+
+	if (!control->final)
+		return -EPROTO;
+
+	clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+	chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
+	chan->rx_state = L2CAP_RX_STATE_RECV;
+	l2cap_process_reqseq(chan, control->reqseq);
+
+	if (!skb_queue_empty(&chan->tx_q))
+		chan->tx_send_head = skb_peek(&chan->tx_q);
+	else
+		chan->tx_send_head = NULL;
+
+	/* Rewind next_tx_seq to the point expected
+	 * by the receiver.
+	 */
+	chan->next_tx_seq = control->reqseq;
+	chan->unacked_frames = 0;
+
+	if (chan->hs_hcon)
+		chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+	else
+		chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+	err = l2cap_resegment(chan);
+
+	if (!err)
+		err = l2cap_rx_state_recv(chan, control, skb, event);
+
+	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 */
@@ -5238,6 +5336,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 			err = l2cap_rx_state_srej_sent(chan, control, skb,
 						       event);
 			break;
+		case L2CAP_RX_STATE_WAIT_P:
+			err = l2cap_rx_state_wait_p(chan, control, skb, event);
+			break;
+		case L2CAP_RX_STATE_WAIT_F:
+			err = l2cap_rx_state_wait_f(chan, control, skb, event);
+			break;
 		default:
 			/* shut it down */
 			break;
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 07/19] Bluetooth: Add move channel confirm handling
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

After sending a move channel response, a move responder waits for a
move channel confirm command.  If the received command has a
"confirmed" result the move is proceeding, and "unconfirmed" means the
move has failed and the channel will not change controllers.

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

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9663292..ed2c23f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1036,6 +1036,42 @@ static void l2cap_move_setup(struct l2cap_chan *chan)
 	set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 }
 
+static void l2cap_move_success(struct l2cap_chan *chan)
+{
+	BT_DBG("chan %p", chan);
+
+	if (chan->mode != L2CAP_MODE_ERTM)
+		return;
+
+	switch (chan->move_role) {
+	case L2CAP_MOVE_ROLE_INITIATOR:
+		l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+		chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+		break;
+	case L2CAP_MOVE_ROLE_RESPONDER:
+		chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+		break;
+	}
+}
+
+static void l2cap_move_revert(struct l2cap_chan *chan)
+{
+	BT_DBG("chan %p", chan);
+
+	if (chan->mode != L2CAP_MODE_ERTM)
+		return;
+
+	switch (chan->move_role) {
+	case L2CAP_MOVE_ROLE_INITIATOR:
+		l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+		chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+		break;
+	case L2CAP_MOVE_ROLE_RESPONDER:
+		chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+		break;
+	}
+}
+
 static void l2cap_chan_ready(struct l2cap_chan *chan)
 {
 	/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -4302,11 +4338,12 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
 	return 0;
 }
 
-static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
-					     struct l2cap_cmd_hdr *cmd,
-					     u16 cmd_len, void *data)
+static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+				      struct l2cap_cmd_hdr *cmd,
+				      u16 cmd_len, void *data)
 {
 	struct l2cap_move_chan_cfm *cfm = data;
+	struct l2cap_chan *chan;
 	u16 icid, result;
 
 	if (cmd_len != sizeof(*cfm))
@@ -4317,8 +4354,35 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
 
 	BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
 
+	chan = l2cap_get_chan_by_dcid(conn, icid);
+	if (!chan)
+		goto send_move_confirm_response;
+
+	if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
+		chan->move_state = L2CAP_MOVE_STABLE;
+		if (result == L2CAP_MC_CONFIRMED) {
+			chan->local_amp_id = chan->move_id;
+			if (!chan->local_amp_id) {
+				/* Have moved off of AMP, free the channel */
+				chan->hs_hchan = NULL;
+				chan->hs_hcon = NULL;
+
+				/* Placeholder - free the logical link */
+			}
+			l2cap_move_success(chan);
+		} else {
+			chan->move_id = chan->local_amp_id;
+			l2cap_move_revert(chan);
+		}
+		chan->move_role = L2CAP_MOVE_ROLE_NONE;
+	}
+
+send_move_confirm_response:
 	l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
 
+	if (chan)
+		l2cap_chan_unlock(chan);
+
 	return 0;
 }
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 08/19] Bluetooth: Add state to hci_chan
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

On an AMP controller, hci_chan maps to a logical link.  When a channel
is being moved, the logical link may or may not be connected already.
The hci_chan->state is used to determine the existance of a useable
logical link so the link can be either used or requested.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 include/net/bluetooth/hci_core.h | 1 +
 net/bluetooth/hci_conn.c         | 1 +
 2 files changed, 2 insertions(+)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 9fe8e2d..00abc52 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -355,6 +355,7 @@ struct hci_chan {
 	struct hci_conn *conn;
 	struct sk_buff_head data_q;
 	unsigned int	sent;
+	__u8		state;
 };
 
 extern struct list_head hci_dev_list;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fe64621..6dcf452 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -959,6 +959,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
 
 	chan->conn = conn;
 	skb_queue_head_init(&chan->data_q);
+	chan->state = BT_CONNECTED;
 
 	list_add_rcu(&chan->list, &conn->chan_list);
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 09/19] Bluetooth: Move channel response
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

The move response command includes a result code indicationg
"pending", "success", or "failure" status.  A pending result is
received when the remote address is still setting up a physical link,
and will be followed by success or failure.  On success, logical link
setup will proceed.  On failure, the move is stopped.  The receiver of
a move channel response must always follow up by sending a move
channel confirm command.

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

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 6d3615e..b4c3c65 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,8 @@
 #define L2CAP_ENC_TIMEOUT		msecs_to_jiffies(5000)
 #define L2CAP_CONN_TIMEOUT		msecs_to_jiffies(40000)
 #define L2CAP_INFO_TIMEOUT		msecs_to_jiffies(4000)
+#define L2CAP_MOVE_TIMEOUT		msecs_to_jiffies(4000)
+#define L2CAP_MOVE_ERTX_TIMEOUT		msecs_to_jiffies(60000)
 
 #define L2CAP_A2MP_DEFAULT_MTU		670
 
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ed2c23f..5e4796c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -128,6 +128,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
 	return NULL;
 }
 
+static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
+						  u8 ident)
+{
+	struct l2cap_chan *c;
+
+	mutex_lock(&conn->chan_lock);
+	c = __l2cap_get_chan_by_ident(conn, ident);
+	if (c)
+		l2cap_chan_lock(c);
+	mutex_unlock(&conn->chan_lock);
+
+	return c;
+}
+
 static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
 {
 	struct l2cap_chan *c;
@@ -4227,6 +4241,13 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
 	l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
 }
 
+static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
+			      u8 status)
+{
+	/* Placeholder */
+	return;
+}
+
 static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 					 struct l2cap_cmd_hdr *cmd,
 					 u16 cmd_len, void *data)
@@ -4317,9 +4338,137 @@ send_move_response:
 	return 0;
 }
 
-static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
-					 struct l2cap_cmd_hdr *cmd,
-					 u16 cmd_len, void *data)
+static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
+{
+	struct l2cap_chan *chan;
+
+	chan = l2cap_get_chan_by_scid(conn, icid);
+	if (!chan) {
+		l2cap_send_move_chan_cfm(conn, NULL, icid,
+					 L2CAP_MC_UNCONFIRMED);
+		return;
+	}
+
+	__clear_chan_timer(chan);
+	if (result == L2CAP_MR_PEND)
+		__set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
+
+	if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP) {
+		/* Move confirm will be sent when logical link
+		 * is complete.
+		 */
+		chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+	} else if (chan->move_state == L2CAP_MOVE_WAIT_RSP_SUCCESS) {
+		if (result == L2CAP_MR_PEND) {
+			goto done;
+		} else if (test_bit(CONN_LOCAL_BUSY,
+				    &chan->conn_state)) {
+			chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+		} else {
+			/* Logical link is up or moving to BR/EDR,
+			 * proceed with move */
+			chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+			l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+						 L2CAP_MC_CONFIRMED);
+			__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+		}
+	} else if (chan->move_state == L2CAP_MOVE_WAIT_RSP) {
+		struct hci_chan *hchan = NULL;
+		/* Moving to AMP */
+		if (result == L2CAP_MR_SUCCESS) {
+			/* Remote is ready, send confirm immediately
+			 * after logical link is ready
+			 */
+			chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+		} else {
+			/* Both logical link and move success
+			 * are required to confirm
+			 */
+			chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
+		}
+
+		/* Placeholder - get hci_chan for logical link */
+		if (!hchan) {
+			/* Logical link not available */
+			l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+						 L2CAP_MC_UNCONFIRMED);
+			goto done;
+		}
+
+		/* If the logical link is not yet connected, do not
+		 * send confirmation.
+		 */
+		if (hchan->state != BT_CONNECTED)
+			goto done;
+
+		/* Logical link is already ready to go */
+
+		chan->hs_hcon = hchan->conn;
+		chan->hs_hcon->l2cap_data = chan->conn;
+
+		if (result == L2CAP_MR_SUCCESS) {
+			/* Can confirm now */
+			l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+						 L2CAP_MC_CONFIRMED);
+		} else {
+			/* Now only need move success
+			 * to confirm
+			 */
+			chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+		}
+
+		l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+	} else {
+		/* Any other amp move state means the move failed. */
+		chan->move_id = chan->local_amp_id;
+		chan->move_state = L2CAP_MOVE_STABLE;
+		l2cap_move_revert(chan);
+		chan->move_role = L2CAP_MOVE_ROLE_NONE;
+		l2cap_send_move_chan_cfm(conn, chan, chan->scid,
+					 L2CAP_MC_UNCONFIRMED);
+		__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+	}
+
+done:
+	l2cap_chan_unlock(chan);
+}
+
+static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
+			    u16 result)
+{
+	struct l2cap_chan *chan;
+
+	chan = l2cap_get_chan_by_ident(conn, ident);
+	if (!chan) {
+		/* Could not locate channel, icid is best guess */
+		l2cap_send_move_chan_cfm(conn, NULL, icid,
+					 L2CAP_MC_UNCONFIRMED);
+		return;
+	}
+
+	__clear_chan_timer(chan);
+
+	if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+		if (result == L2CAP_MR_COLLISION) {
+			chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+		} else {
+			/* Cleanup - cancel move */
+			chan->move_id = chan->local_amp_id;
+			chan->move_state = L2CAP_MOVE_STABLE;
+			l2cap_move_revert(chan);
+			chan->move_role = L2CAP_MOVE_ROLE_NONE;
+		}
+	}
+
+	l2cap_send_move_chan_cfm(conn, chan, chan->scid, L2CAP_MC_UNCONFIRMED);
+	__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+
+	l2cap_chan_unlock(chan);
+}
+
+static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
+				  struct l2cap_cmd_hdr *cmd,
+				  u16 cmd_len, void *data)
 {
 	struct l2cap_move_chan_rsp *rsp = data;
 	u16 icid, result;
@@ -4332,8 +4481,10 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
 
 	BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
 
-	/* Placeholder: Always unconfirmed */
-	l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+	if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
+		l2cap_move_continue(conn, icid, result);
+	else
+		l2cap_move_fail(conn, cmd->ident, icid, result);
 
 	return 0;
 }
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 10/19] Bluetooth: Add logical link confirm
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

The logical link confirm callback is executed when the AMP controller
completes its logical link setup.  During a channel move, a newly
formed logical link allows a move responder to send a move channel
response.  A move initiator will send a move channel confirm.  A
failed logical link will end the channel move and send an appropriate
response or confirm command indicating a failure.

If the channel is being created on an AMP controller, L2CAP
configuration is started after the logical link is set up.

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

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5e4796c..ddd8c72 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3799,6 +3799,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
 		goto unlock;
 	}
 
+	chan->ident = cmd->ident;
 	l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
 	chan->num_conf_rsp++;
 
@@ -4241,11 +4242,120 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
 	l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
 }
 
+/* Call with chan locked */
 static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 			      u8 status)
 {
-	/* Placeholder */
-	return;
+	BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
+
+	if (status) {
+		/* Logical link setup failed */
+		if (chan->state != BT_CONNECTED) {
+			/* Create channel failure, disconnect */
+			l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+		} else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+			l2cap_move_revert(chan);
+			chan->move_role = L2CAP_MOVE_ROLE_NONE;
+			chan->move_state = L2CAP_MOVE_STABLE;
+			l2cap_send_move_chan_rsp(chan->conn, chan->ident,
+						 chan->dcid, L2CAP_MR_NOT_SUPP);
+		} else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+			if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
+			    chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
+				/* Remote has only sent pending or
+				 * success responses, clean up
+				 */
+				l2cap_move_revert(chan);
+				chan->move_role = L2CAP_MOVE_ROLE_NONE;
+				chan->move_state = L2CAP_MOVE_STABLE;
+			}
+
+			/* Other amp move states imply that the move
+			 * has already aborted
+			 */
+			l2cap_send_move_chan_cfm(chan->conn, chan, chan->scid,
+						 L2CAP_MC_UNCONFIRMED);
+			__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+		}
+
+		chan->hs_hchan = NULL;
+		chan->hs_hcon = NULL;
+
+		/* Placeholder - free logical link */
+
+	} else if (chan->state != BT_CONNECTED) {
+		struct l2cap_conf_rsp rsp;
+		u8 code;
+
+		/* Create channel complete */
+
+		/* Ignore logical link if channel is on BR/EDR */
+		if (!chan->local_amp_id)
+			return;
+
+		chan->hs_hcon = hchan->conn;
+		chan->hs_hcon->l2cap_data = chan->conn;
+
+		code = l2cap_build_conf_rsp(chan, &rsp,
+					    L2CAP_CONF_SUCCESS, 0);
+		l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CONF_RSP, code,
+			       &rsp);
+		set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+		if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
+			int err = 0;
+
+			set_default_fcs(chan);
+
+			err = l2cap_ertm_init(chan);
+			if (err < 0)
+				l2cap_send_disconn_req(chan->conn, chan, -err);
+			else
+				l2cap_chan_ready(chan);
+		}
+	} else {
+		/* Channel move */
+		chan->hs_hcon = hchan->conn;
+		chan->hs_hcon->l2cap_data = chan->conn;
+
+		BT_DBG("move_state %d", chan->move_state);
+
+		switch (chan->move_state) {
+		case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+			/* Move confirm will be sent after a success
+			 * response is received
+			 */
+			chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+			break;
+		case L2CAP_MOVE_WAIT_LOGICAL_CFM:
+			if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+				chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+			} else if (chan->move_role ==
+				   L2CAP_MOVE_ROLE_INITIATOR) {
+				chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+				l2cap_send_move_chan_cfm(chan->conn, chan,
+							 chan->scid,
+							 L2CAP_MR_SUCCESS);
+				__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+			} else if (chan->move_role ==
+				   L2CAP_MOVE_ROLE_RESPONDER) {
+				chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+				l2cap_send_move_chan_rsp(chan->conn,
+							 chan->ident,
+							 chan->dcid,
+							 L2CAP_MR_SUCCESS);
+			}
+			break;
+		default:
+			/* Move was not in expected state, free the channel */
+			chan->hs_hchan = NULL;
+			chan->hs_hcon = NULL;
+
+			/* Placeholder - free the logical link */
+
+			chan->move_state = L2CAP_MOVE_STABLE;
+		}
+	}
 }
 
 static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 11/19] Bluetooth: Add move confirm response handling
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

The move confirm response concludes the channel move command sequence.
Receipt of this command indicates that data may begin to flow again.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ddd8c72..2aaa9da 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4652,6 +4652,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
 						 u16 cmd_len, void *data)
 {
 	struct l2cap_move_chan_cfm_rsp *rsp = data;
+	struct l2cap_chan *chan;
 	u16 icid;
 
 	if (cmd_len != sizeof(*rsp))
@@ -4661,6 +4662,31 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
 
 	BT_DBG("icid 0x%4.4x", icid);
 
+	chan = l2cap_get_chan_by_scid(conn, icid);
+	if (!chan)
+		return 0;
+
+	__clear_chan_timer(chan);
+
+	if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
+		chan->move_state = L2CAP_MOVE_STABLE;
+		chan->local_amp_id = chan->move_id;
+
+		if (!chan->local_amp_id && chan->hs_hchan) {
+			/* Have moved off of AMP, free the channel */
+			chan->hs_hchan = NULL;
+			chan->hs_hcon = NULL;
+
+			/* Placeholder - free the logical link */
+		}
+
+		l2cap_move_success(chan);
+
+		chan->move_role = L2CAP_MOVE_ROLE_NONE;
+	}
+
+	l2cap_chan_unlock(chan);
+
 	return 0;
 }
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 12/19] Bluetooth: Handle physical link completion
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Several different actions may be taken when an AMP physical link
becomes available.  A channel being created on an AMP controller must
continue the connection process.  A channel being moved needs to
either send a move request or a move response.  A failed physical link
will revert to using a BR/EDR controller if possible.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 166 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2aaa9da..a6cf657 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1015,6 +1015,19 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
 	l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
 }
 
+static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
+{
+	struct l2cap_create_chan_req req;
+	req.scid = cpu_to_le16(chan->scid);
+	req.psm  = chan->psm;
+	req.amp_id = amp_id;
+
+	chan->ident = l2cap_get_ident(chan->conn);
+
+	l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
+		       sizeof(req), &req);
+}
+
 static void l2cap_move_setup(struct l2cap_chan *chan)
 {
 	struct sk_buff *skb;
@@ -4199,6 +4212,23 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
 	return 0;
 }
 
+static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
+{
+	struct l2cap_move_chan_req req;
+	u8 ident;
+
+	BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
+
+	ident = l2cap_get_ident(chan->conn);
+	chan->ident = ident;
+
+	req.icid = cpu_to_le16(chan->scid);
+	req.dest_amp_id = dest_amp_id;
+
+	l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
+		       &req);
+}
+
 static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
 				     u16 icid, u16 result)
 {
@@ -4358,6 +4388,142 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 	}
 }
 
+static void l2cap_do_create(struct l2cap_chan *chan, int result,
+			    u8 local_amp_id, u8 remote_amp_id)
+{
+	if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
+		struct l2cap_conn_rsp rsp;
+		char buf[128];
+		rsp.scid = cpu_to_le16(chan->dcid);
+		rsp.dcid = cpu_to_le16(chan->scid);
+
+		/* Incoming channel on AMP */
+		if (result == L2CAP_CR_SUCCESS) {
+			/* Send successful response */
+			rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+			rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+		} else {
+			/* Send negative response */
+			rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM);
+			rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+		}
+
+		l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
+			       sizeof(rsp), &rsp);
+
+		if (result == L2CAP_CR_SUCCESS) {
+			__l2cap_state_change(chan, BT_CONFIG);
+			set_bit(CONF_REQ_SENT, &chan->conf_state);
+			l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
+				       L2CAP_CONF_REQ,
+				       l2cap_build_conf_req(chan, buf), buf);
+			chan->num_conf_req++;
+		}
+	} else {
+		/* Outgoing channel on AMP */
+		if (result == L2CAP_CR_SUCCESS) {
+			chan->local_amp_id = local_amp_id;
+			l2cap_send_create_chan_req(chan, remote_amp_id);
+		} else {
+			/* Revert to BR/EDR connect */
+			l2cap_send_conn_req(chan);
+		}
+	}
+}
+
+static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
+				   u8 remote_amp_id)
+{
+	l2cap_move_setup(chan);
+	chan->move_id = local_amp_id;
+	chan->move_state = L2CAP_MOVE_WAIT_RSP;
+
+	l2cap_send_move_chan_req(chan, remote_amp_id);
+	__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+}
+
+static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
+{
+	struct hci_chan *hchan = NULL;
+
+	/* Placeholder - get hci_chan for logical link */
+
+	if (hchan) {
+		if (hchan->state == BT_CONNECTED) {
+			/* Logical link is ready to go */
+			chan->hs_hcon = hchan->conn;
+			chan->hs_hcon->l2cap_data = chan->conn;
+			chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+			l2cap_send_move_chan_rsp(chan->conn, chan->ident,
+						 chan->dcid, L2CAP_MR_SUCCESS);
+
+			l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+		} else {
+			/* Wait for logical link to be ready */
+			chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+		}
+	} else {
+		/* Logical link not available */
+		l2cap_send_move_chan_rsp(chan->conn, chan->ident, chan->dcid,
+					 L2CAP_MR_NOT_ALLOWED);
+	}
+}
+
+static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
+{
+	if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+		u8 rsp_result;
+		if (result == -EINVAL)
+			rsp_result = L2CAP_MR_BAD_ID;
+		else
+			rsp_result = L2CAP_MR_NOT_ALLOWED;
+
+		l2cap_send_move_chan_rsp(chan->conn, chan->ident, chan->dcid,
+					 rsp_result);
+	}
+
+	chan->move_role = L2CAP_MOVE_ROLE_NONE;
+	chan->move_state = L2CAP_MOVE_STABLE;
+
+	/* Restart data transmission */
+	l2cap_ertm_send(chan);
+}
+
+void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id,
+			u8 remote_amp_id)
+{
+	BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
+	       chan, result, local_amp_id, remote_amp_id);
+
+	l2cap_chan_lock(chan);
+
+	if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
+		l2cap_chan_unlock(chan);
+		return;
+	}
+
+	if (chan->state != BT_CONNECTED) {
+		l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
+	} else if (result != L2CAP_MR_SUCCESS) {
+		l2cap_do_move_cancel(chan, result);
+	} else {
+		switch (chan->move_role) {
+		case L2CAP_MOVE_ROLE_INITIATOR:
+			l2cap_do_move_initiate(chan, local_amp_id,
+					       remote_amp_id);
+			break;
+		case L2CAP_MOVE_ROLE_RESPONDER:
+			l2cap_do_move_respond(chan, result);
+			break;
+		default:
+			l2cap_do_move_cancel(chan, result);
+			break;
+		}
+	}
+
+	l2cap_chan_unlock(chan);
+}
+
 static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 					 struct l2cap_cmd_hdr *cmd,
 					 u16 cmd_len, void *data)
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 13/19] Bluetooth: Flag ACL frames as complete for AMP controllers
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

AMP controllers expect to transmit only "complete" ACL frames.  These
frames have both the "start" and "cont" bits set.  AMP does not allow
fragmented ACLs.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a6cf657..cb85bdd 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -762,6 +762,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
 	BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
 	       skb->priority);
 
+	if (chan->hs_hcon && !__chan_is_moving(chan)) {
+		if (chan->hs_hchan)
+			hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
+		else
+			kfree_skb(skb);
+
+		return;
+	}
+
 	if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
 	    lmp_no_flush_capable(hcon->hdev))
 		flags = ACL_START_NO_FLUSH;
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 14/19] Bluetooth: Do not send data during channel move
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Outgoing ERTM data is queued during a channel move.  The ERTM state
machine is partially reset at the start of a move, and must be
resynchronized with the remote state machine at the end of the move.
Data is not sent so that there are no state transitions between the
partial reset and the resync.

Streaming mode frames are dropped during a move.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cb85bdd..5c7681d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -946,6 +946,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
 	if (!control->sframe)
 		return;
 
+	if (__chan_is_moving(chan))
+		return;
+
 	if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
 	    !control->poll)
 		control->final = 1;
@@ -1824,6 +1827,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
 
 	BT_DBG("chan %p, skbs %p", chan, skbs);
 
+	if (__chan_is_moving(chan))
+		return;
+
 	skb_queue_splice_tail_init(skbs, &chan->tx_q);
 
 	while (!skb_queue_empty(&chan->tx_q)) {
@@ -1866,6 +1872,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
 	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
 		return 0;
 
+	if (__chan_is_moving(chan))
+		return 0;
+
 	while (chan->tx_send_head &&
 	       chan->unacked_frames < chan->remote_tx_win &&
 	       chan->tx_state == L2CAP_TX_STATE_XMIT) {
@@ -1931,6 +1940,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
 	if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
 		return;
 
+	if (__chan_is_moving(chan))
+		return;
+
 	while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
 		seq = l2cap_seq_list_pop(&chan->retrans_list);
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 15/19] Bluetooth: Configure appropriate timeouts for AMP controllers
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

The L2CAP spec recommends specific retransmit and monitor timeouts for
ERTM channels that are on AMP controllers.  These timeouts are
calculated from the AMP controller's best effort flush timeout.

BR/EDR controllers use the default retransmit and monitor timeouts.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 47 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5c7681d..14991d8 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2980,6 +2980,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
 	return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
 }
 
+static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
+				      struct l2cap_conf_rfc *rfc)
+{
+	if (chan->local_amp_id && chan->hs_hcon) {
+		u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
+
+		/* Class 1 devices have must have ERTM timeouts
+		 * exceeding the Link Supervision Timeout.  The
+		 * default Link Supervision Timeout for AMP
+		 * controllers is 10 seconds.
+		 *
+		 * Class 1 devices use 0xffffffff for their
+		 * best-effort flush timeout, so the clamping logic
+		 * will result in a timeout that meets the above
+		 * requirement.  ERTM timeouts are 16-bit values, so
+		 * the maximum timeout is 65.535 seconds.
+		 */
+
+		/* Convert timeout to milliseconds and round */
+		ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000);
+
+		/* This is the recommended formula for class 2 devices
+		 * that start ERTM timers when packets are sent to the
+		 * controller.
+		 */
+		ertm_to = 3 * ertm_to + 500;
+
+		if (ertm_to > 0xffff)
+			ertm_to = 0xffff;
+
+		rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
+		rfc->monitor_timeout = rfc->retrans_timeout;
+	} else {
+		rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+		rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+	}
+}
+
 static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
 {
 	if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
@@ -3046,8 +3084,8 @@ done:
 	case L2CAP_MODE_ERTM:
 		rfc.mode            = L2CAP_MODE_ERTM;
 		rfc.max_transmit    = chan->max_tx;
-		rfc.retrans_timeout = 0;
-		rfc.monitor_timeout = 0;
+
+		__l2cap_set_ertm_timeouts(chan, &rfc);
 
 		size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
 			     L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
@@ -3275,10 +3313,7 @@ done:
 			rfc.max_pdu_size = cpu_to_le16(size);
 			chan->remote_mps = size;
 
-			rfc.retrans_timeout =
-				__constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
-			rfc.monitor_timeout =
-				__constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+			__l2cap_set_ertm_timeouts(chan, &rfc);
 
 			set_bit(CONF_MODE_DONE, &chan->conf_state);
 
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 16/19] Bluetooth: Ignore BR/EDR packet size constraints when fragmenting for AMP
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel, Andrei Emeltchenko
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

When operating over BR/EDR, ERTM accounts for the maximum over-the-air
packet size when setting the PDU size.  AMP controllers do not use the
same over-the-air packets, so the PDU size should only be based on the
HCI MTU of the AMP controller.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 14991d8..7e8fe84 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2285,7 +2285,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
 	/* PDU size is derived from the HCI MTU */
 	pdu_len = chan->conn->mtu;
 
-	pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
+	/* Constrain PDU size for BR/EDR connections */
+	if (!chan->hs_hcon)
+		pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
 
 	/* Adjust for largest possible L2CAP overhead. */
 	if (chan->fcs)
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 17/19] Bluetooth: Send create channel request instead of connect for AMP
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

When the channel policy is set to prefer AMP, then an L2CAP channel is
set up using the "create channel" command rather than the "connect"
command.  A physical link is also set up before sending "create
channel".

Behavior is unchanged if enable_hs is false.

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

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 7e8fe84..17d917b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -58,6 +58,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn,
 static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
 		     struct sk_buff_head *skbs, u8 event);
 
+static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
+			      u8 status);
+
 /* ---- L2CAP channels ---- */
 
 static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -576,6 +579,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 			mgr->bredr_chan = NULL;
 	}
 
+	if (chan->hs_hchan) {
+		chan->hs_hchan = NULL;
+		chan->hs_hcon = NULL;
+
+		/* Placeholder - free logical link */
+	}
+
 	chan->ops->teardown(chan, err);
 
 	if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
@@ -1126,6 +1136,7 @@ static void l2cap_start_connection(struct l2cap_chan *chan)
 {
 	if (__amp_capable(chan)) {
 		BT_DBG("chan %p AMP capable: discover AMPs", chan);
+		set_bit(CONF_CONNECT_PEND, &chan->conf_state);
 		a2mp_discover_amp(chan);
 	} else {
 		l2cap_send_conn_req(chan);
@@ -1271,6 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
 				rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
 			}
 
+			if (rsp.result == __constant_cpu_to_le16(L2CAP_CR_SUCCESS) &&
+			    chan->local_amp_id) {
+				/* Placeholder - uncomment when amp functions
+				 * are available
+				amp_accept_physical(chan, chan->local_amp_id);
+				 */
+				l2cap_chan_unlock(chan);
+				continue;
+			}
+
 			l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
 				       sizeof(rsp), &rsp);
 
@@ -3359,6 +3380,18 @@ done:
 			rfc.mode = chan->mode;
 		}
 
+		if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state) &&
+		    chan->local_amp_id) {
+			struct hci_chan *hchan = NULL;
+
+			/* Placeholder - get hci_chan for logical link */
+
+			if (hchan && hchan->state == BT_CONNECTED) {
+				l2cap_logical_cfm(chan, hchan,
+						  L2CAP_MR_SUCCESS);
+			}
+		}
+
 		if (result == L2CAP_CONF_SUCCESS)
 			set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
 	}
@@ -6367,6 +6400,15 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 					stat = L2CAP_CS_AUTHOR_PEND;
 					chan->ops->defer(chan);
 				} else {
+					if (chan->local_amp_id) {
+						/* Placeholder - accept physical
+						 * link
+						amp_accept_physical(chan,
+							chan->local_amp_id);
+						*/
+						continue;
+					}
+
 					__l2cap_state_change(chan, BT_CONFIG);
 					res = L2CAP_CR_SUCCESS;
 					stat = L2CAP_CS_NO_INFO;
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 18/19] Bluetooth: Do not retransmit data during a channel move
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Do not retransmit previously-sent data when a "receiver ready" s-frame
with the "final" flag is received during a move.

The ERTM state machines will resynchronize at the end of a channel
move, and the state machine needs to avoid state changes during a
move.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 net/bluetooth/l2cap_core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 17d917b..2263c1d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5636,8 +5636,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
 		if (control->final) {
 			clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
 
-			if (!test_and_clear_bit(CONN_REJ_ACT,
-						&chan->conn_state)) {
+			if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
+			    !__chan_is_moving(chan)) {
 				control->final = 0;
 				l2cap_retransmit_all(chan, control);
 			}
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* [PATCHv2 19/19] Bluetooth: Start channel move when socket option is changed
From: Mat Martineau @ 2012-10-16 23:36 UTC (permalink / raw)
  To: linux-bluetooth, gustavo; +Cc: sunnyk, marcel
In-Reply-To: <1350430593-23565-1-git-send-email-mathewm@codeaurora.org>

Channel moves are triggered by changes to the BT_CHANNEL_POLICY
sockopt when an ERTM or streaming-mode channel is connected.

Moves are only started if enable_hs is true.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
---
 include/net/bluetooth/l2cap.h |  1 +
 net/bluetooth/l2cap_core.c    | 20 ++++++++++++++++++++
 net/bluetooth/l2cap_sock.c    |  5 +++++
 3 files changed, 26 insertions(+)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b4c3c65..49783e9 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -809,5 +809,6 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void l2cap_chan_del(struct l2cap_chan *chan, int err);
 void l2cap_send_conn_req(struct l2cap_chan *chan);
+void l2cap_move_start(struct l2cap_chan *chan);
 
 #endif /* __L2CAP_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2263c1d..3f6410b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4479,6 +4479,26 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 	}
 }
 
+void l2cap_move_start(struct l2cap_chan *chan)
+{
+	BT_DBG("chan %p", chan);
+
+	if (chan->local_amp_id == 0) {
+		if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
+			return;
+		chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+		chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+		/* Placeholder - start physical link setup */
+	} else {
+		chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+		chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+		chan->move_id = 0;
+		l2cap_move_start(chan);
+		l2cap_send_move_chan_req(chan, 0);
+		__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+	}
+}
+
 static void l2cap_do_create(struct l2cap_chan *chan, int result,
 			    u8 local_amp_id, u8 remote_amp_id)
 {
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 5fae2bd..7cb4d73 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -735,6 +735,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 		}
 
 		chan->chan_policy = (u8) opt;
+
+		if (sk->sk_state == BT_CONNECTED &&
+		    chan->move_role == L2CAP_MOVE_ROLE_NONE)
+			l2cap_move_start(chan);
+
 		break;
 
 	default:
-- 
1.7.12.3

--
Mat Martineau

Employee of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related

* Re: [PATCH] avdtp: Fix crash on connection lost
From: Szymon Janc @ 2012-10-17  7:28 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZLbX__mxzwvZMOd3noCeoJTRrXshFTEovM9Tco4bziLRg@mail.gmail.com>

On Tuesday 16 of October 2012 20:34:47 Luiz Augusto von Dentz wrote:
> Hi Szymon,

Hi Luiz,

> >         if (session->io) {
> >                 g_io_channel_shutdown(session->io, FALSE, NULL);
> > @@ -1173,8 +1172,6 @@ static void connection_lost(struct avdtp *session, int err)
> >                 session->io = NULL;
> >         }
> >
> > -       avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
> 
> This is actually the one that we need in case we got disconnect by
> remote after discovery completes. avctp.c has a similar structure to
> handler such states, but without the refcounting all is much simpler.

OK

> 
> >         if (session->io_id) {
> >                 g_source_remove(session->io_id);
> >                 session->io_id = 0;
> > @@ -1221,9 +1218,6 @@ void avdtp_unref(struct avdtp *session)
> >
> >                 if (session->io)
> >                         set_disconnect_timer(session);
> > -               else if (!session->free_lock) /* Drop the local ref if we
> > -                                                aren't connected */
> > -                       session->ref--;
> >         }
> >
> >         if (session->ref > 0)
> > --
> > 1.7.9.5
> 
> We should probably get rid of the free_lock as well.

Well, this is what this patch does, doesn't it?:)

-- 
BR
Szymon Janc

^ permalink raw reply

* [PATCH] heartrate: Always write measurement CCC on discovery
From: Andrzej Kaczmarek @ 2012-10-17  7:51 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek

Measurement CCC is written during discovery only in case watcher is
registered and notifications shall be enabled. However, it may happen
that (e.g. during reconnection scenario) notifications are already
enabled on remote but watcher is no longer registered and we will
be receiving unwanted notifications.

This patch makes sure measurement CCC is written to proper value
every time it's discovered during connection.
---
 profiles/heartrate/heartrate.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index 9a92960..bcdce8b 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -412,28 +412,30 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu,
 	for (i = 0; i < list->num; i++) {
 		uint8_t *value;
 		uint16_t handle, uuid;
+		char *msg;
+		uint8_t attr_val[2];
 
 		value = list->data[i];
 		handle = att_get_u16(value);
 		uuid = att_get_u16(value + 2);
 
-		if (uuid == GATT_CLIENT_CHARAC_CFG_UUID) {
-			char *msg;
-			uint8_t value[2];
+		if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+			continue;
 
-			hr->measurement_ccc_handle = handle;
+		hr->measurement_ccc_handle = handle;
 
-			if (g_slist_length(hr->hradapter->watchers) == 0)
-				break;
-
-			att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+		if (g_slist_length(hr->hradapter->watchers) == 0) {
+			att_put_u16(0x0000, attr_val);
+			msg = g_strdup("Disable measurement");
+		} else {
+			att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
 			msg = g_strdup("Enable measurement");
+		}
 
-			gatt_write_char(hr->attrib, handle, value,
-					sizeof(value), char_write_cb, msg);
+		gatt_write_char(hr->attrib, handle, attr_val,
+				sizeof(attr_val), char_write_cb, msg);
 
-			break;
-		}
+		break;
 	}
 
 done:
-- 
1.7.11.3


^ permalink raw reply related

* Re: [PATCH] heartrate: Always write measurement CCC on discovery
From: Johan Hedberg @ 2012-10-17  8:09 UTC (permalink / raw)
  To: Andrzej Kaczmarek; +Cc: linux-bluetooth
In-Reply-To: <1350460293-31316-1-git-send-email-andrzej.kaczmarek@tieto.com>

Hi Andrzej,

On Wed, Oct 17, 2012, Andrzej Kaczmarek wrote:
> Measurement CCC is written during discovery only in case watcher is
> registered and notifications shall be enabled. However, it may happen
> that (e.g. during reconnection scenario) notifications are already
> enabled on remote but watcher is no longer registered and we will
> be receiving unwanted notifications.
> 
> This patch makes sure measurement CCC is written to proper value
> every time it's discovered during connection.
> ---
>  profiles/heartrate/heartrate.c | 26 ++++++++++++++------------
>  1 file changed, 14 insertions(+), 12 deletions(-)

Applied. Thanks.

Johan

^ permalink raw reply

* Re: [PATCH] avdtp: Fix crash on connection lost
From: Luiz Augusto von Dentz @ 2012-10-17  8:48 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <15890511.ludKIz2NbJ@uw000953>

[-- Attachment #1: Type: text/plain, Size: 1770 bytes --]

Hi Szymon,

On Wed, Oct 17, 2012 at 9:28 AM, Szymon Janc <szymon.janc@tieto.com> wrote:
> On Tuesday 16 of October 2012 20:34:47 Luiz Augusto von Dentz wrote:
>> Hi Szymon,
>
> Hi Luiz,
>
>> >         if (session->io) {
>> >                 g_io_channel_shutdown(session->io, FALSE, NULL);
>> > @@ -1173,8 +1172,6 @@ static void connection_lost(struct avdtp *session, int err)
>> >                 session->io = NULL;
>> >         }
>> >
>> > -       avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
>>
>> This is actually the one that we need in case we got disconnect by
>> remote after discovery completes. avctp.c has a similar structure to
>> handler such states, but without the refcounting all is much simpler.
>
> OK
>
>>
>> >         if (session->io_id) {
>> >                 g_source_remove(session->io_id);
>> >                 session->io_id = 0;
>> > @@ -1221,9 +1218,6 @@ void avdtp_unref(struct avdtp *session)
>> >
>> >                 if (session->io)
>> >                         set_disconnect_timer(session);
>> > -               else if (!session->free_lock) /* Drop the local ref if we
>> > -                                                aren't connected */
>> > -                       session->ref--;
>> >         }
>> >
>> >         if (session->ref > 0)
>> > --
>> > 1.7.9.5
>>
>> We should probably get rid of the free_lock as well.
>
> Well, this is what this patch does, doesn't it?:)

What about the attached patch, it should fix the references for good
by using the dc_timer when the reference drops to 0, I just need to
check if 1 second is enough to cover all cases including the ones
where the remote device connects but doesn't setup any stream, with
the devices I have 1 second seems enough.

-- 
Luiz Augusto von Dentz

[-- Attachment #2: 0001-AVDTP-Do-not-keep-a-internal-reference.patch --]
[-- Type: application/octet-stream, Size: 5527 bytes --]

From 39312cf09fac700505deb7babd129bec1b4e26dc Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 16 Oct 2012 23:09:53 +0300
Subject: [PATCH BlueZ] AVDTP: Do not keep a internal reference

Don't initialize reference with 1, instead always start disconnect timer
when reference drops to 0, so in case nobody reclaims the session it
automatically disconnect after 1 second and frees the memory.
---
 audio/avdtp.c | 114 ++++++++++++++++++++++++++--------------------------------
 1 file changed, 50 insertions(+), 64 deletions(-)

diff --git a/audio/avdtp.c b/audio/avdtp.c
index bd91cb6..e35b324 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -388,7 +388,6 @@ struct avdtp_stream {
 
 struct avdtp {
 	int ref;
-	int free_lock;
 
 	uint16_t version;
 
@@ -780,8 +779,9 @@ static void avdtp_set_state(struct avdtp *session,
 	}
 }
 
-static void stream_free(struct avdtp_stream *stream)
+static void stream_free(void *data)
 {
+	struct avdtp_stream *stream = data;
 	struct avdtp_remote_sep *rsep;
 
 	stream->lsep->info.inuse = 0;
@@ -1148,33 +1148,30 @@ static int avdtp_cancel_authorization(struct avdtp *session)
 	return 0;
 }
 
-static void connection_lost(struct avdtp *session, int err)
+static void sep_free(gpointer data)
 {
-	char address[18];
-
-	ba2str(&session->dst, address);
-	DBG("Disconnected from %s", address);
+	struct avdtp_remote_sep *sep = data;
 
-	if (err != EACCES)
-		avdtp_cancel_authorization(session);
+	g_slist_free_full(sep->caps, g_free);
+	g_free(sep);
+}
 
-	session->free_lock = 1;
+static void avdtp_free(struct avdtp *session)
+{
+	struct avdtp_server *server;
 
-	finalize_discovery(session, err);
+	DBG("%p", session);
 
-	g_slist_foreach(session->streams, (GFunc) release_stream, session);
-	session->streams = NULL;
+	g_slist_free_full(session->streams, stream_free);
 
-	session->free_lock = 0;
+	if (session->state != AVDTP_SESSION_STATE_DISCONNECTED)
+		avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
 
 	if (session->io) {
 		g_io_channel_shutdown(session->io, FALSE, NULL);
 		g_io_channel_unref(session->io);
-		session->io = NULL;
 	}
 
-	avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
-
 	if (session->io_id) {
 		g_source_remove(session->io_id);
 		session->io_id = 0;
@@ -1183,69 +1180,58 @@ static void connection_lost(struct avdtp *session, int err)
 	if (session->dc_timer)
 		remove_disconnect_timer(session);
 
-	if (session->ref != 1)
-		error("connection_lost: ref count not 1 after all callbacks");
-	else
-		avdtp_unref(session);
-}
+	avdtp_cancel_authorization(session);
 
-static void sep_free(gpointer data)
-{
-	struct avdtp_remote_sep *sep = data;
+	server = session->server;
 
-	g_slist_free_full(sep->caps, g_free);
-	g_free(sep);
-}
+	server->sessions = g_slist_remove(server->sessions, session);
 
-void avdtp_unref(struct avdtp *session)
-{
-	struct avdtp_server *server;
+	if (session->req)
+		pending_req_free(session->req);
 
-	if (!session)
-		return;
+	g_slist_free_full(session->seps, sep_free);
 
-	session->ref--;
+	g_free(session->buf);
 
-	DBG("%p: ref=%d", session, session->ref);
+	g_free(session);
+}
 
-	if (session->ref == 1) {
-		if (session->state == AVDTP_SESSION_STATE_CONNECTING &&
-								session->io) {
-			avdtp_cancel_authorization(session);
-			g_io_channel_shutdown(session->io, TRUE, NULL);
-			g_io_channel_unref(session->io);
-			session->io = NULL;
-			avdtp_set_state(session,
-					AVDTP_SESSION_STATE_DISCONNECTED);
-		}
+static void connection_lost(struct avdtp *session, int err)
+{
+	char address[18];
 
-		if (session->io)
-			set_disconnect_timer(session);
-		else if (!session->free_lock) /* Drop the local ref if we
-						 aren't connected */
-			session->ref--;
-	}
+	ba2str(&session->dst, address);
+	DBG("Disconnected from %s", address);
 
-	if (session->ref > 0)
+	if (err != EACCES)
+		avdtp_cancel_authorization(session);
+
+	if (session->ref == 0) {
+		avdtp_free(session);
 		return;
+	}
 
-	server = session->server;
+	g_slist_foreach(session->streams, (GFunc) release_stream, session);
+	session->streams = NULL;
 
-	DBG("%p: freeing session and removing from list", session);
+	finalize_discovery(session, err);
 
-	if (session->dc_timer)
-		remove_disconnect_timer(session);
+	avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+}
 
-	server->sessions = g_slist_remove(server->sessions, session);
+void avdtp_unref(struct avdtp *session)
+{
+	if (!session)
+		return;
 
-	if (session->req)
-		pending_req_free(session->req);
+	session->ref--;
 
-	g_slist_free_full(session->seps, sep_free);
+	DBG("%p: ref=%d", session, session->ref);
 
-	g_free(session->buf);
+	if (session->ref > 0)
+		return;
 
-	g_free(session);
+	set_disconnect_timer(session);
 }
 
 struct avdtp *avdtp_ref(struct avdtp *session)
@@ -2231,7 +2217,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 			goto failed;
 		}
 
-		if (session->ref == 1 && !session->streams && !session->req)
+		if (session->ref == 0 && !session->streams && !session->req)
 			set_disconnect_timer(session);
 
 		if (session->streams && session->dc_timer)
@@ -2383,7 +2369,7 @@ static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst
 
 	session->server = server;
 	bacpy(&session->dst, dst);
-	session->ref = 1;
+	set_disconnect_timer(session);
 	/* We don't use avdtp_set_state() here since this isn't a state change
 	 * but just setting of the initial state */
 	session->state = AVDTP_SESSION_STATE_DISCONNECTED;
-- 
1.7.11.4


^ permalink raw reply related

* RE: gatttool and multiple devices connected
From: Kim Schulz @ 2012-10-17  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <507D7480.6040205@schulz.dk>

Den 2012-10-16 16:51, Kim Schulz skrev:
> On 10/16/2012 04:45 PM, Anderson Lizardo wrote:
>> Which kernel version are you running? Older kernels had a LE 
>> connection routing issue which I believe was fixed long ago. And what 
>> do you mean with not being able to get services? which error did you 
>> get? Do you have hcidump logs from these sessions? Regards,
>
> The Bluez code is one taken from git a few days ago. I don't recall 
> which kernel version (will have to check at the office tomorrow) but 
> it is one of the latest that we compiled for this test.
> We do have some logs of the session and I will be looking at those 
> more thoroughly tomorrow.
>
> kim
> --
> To unsubscribe from this list: send the line "unsubscribe 
> linux-bluetooth" in the body of a message to majordomo@vger.kernel.org 
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Hi,
Turns out that the guy compiling our kernel choose a version 
3.4.0.030400 so not really the
bleeding edge version.
Do you have any recommendation on what version of the kernel we should 
aim for?


-- 
Kim Schulz



^ permalink raw reply

* Bluez/Obexd upstream test result_20121017(bluez-4.101.681+ obexd-0.47.66)
From: Li, XiaX @ 2012-10-17  9:58 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org; +Cc: zhouzhy@neusoft.com

[-- Attachment #1: Type: text/plain, Size: 1303 bytes --]

Hi all,

QA finished upstream testing. The test is for bluez-4.101.681 and obexd-0.47.66. Against last, 
BlueZ is changed from 4.101.319 to 4.101.681, Obexd is changed from 0.47.37 to 0.47.66.
In this test, totally ran 100 case: 72 Pass, 10 Fail. Other 18 are blocked or unavailable (some 
cases validation method is still in investigating). 
The pass rate is 88% (pass / <pass + fail>). Ofono has not caught up with the BlueZ API changing in HFP profile.

New bugs:
===============================================
25748 - [HFP]Ofono list-modems shows no modem after paired with phone.
https://bugs.meego.com/show_bug.cgi?id=25748

Re-open bugs:
===============================================
No

Verified bugs:
===============================================
No

Testing Environment
==============================================
Hardware:  netbook Eeepc 1005HA | Acer AspireOne NAV50
Image:     netbook-ia32-pinetrail-tizen_20120424.2
Linux Kernel: v3.4-rc7
bluez-4.101.681.g6f4d4e0-1.1.i586
obexd-0.47.66.g2281d4f-2.1.i586
obexd-server-0.47.66.g2281d4f-2.1.i586
connman-1.8.6.g9f0fc79-4.1.i586
ofono-1.11.114.g73c69ee-2.1.i586
Pulseaudio: 67602d8743e8e529919bb9fbb956aa77724a8e50 (oct, 6th)

For detailed test results, please see attached file.

Thanks
Li Xia


[-- Attachment #2: Bluetooth_upstream_quality_20121017.txt --]
[-- Type: text/plain, Size: 7554 bytes --]

== Detail Result ==
[PASS]: case passed <br>
[FAIL]: case failed <br>
[Block]: case is blocked by one bug <br>
[N/A]: case not available to be tested

=== Audio-A2DP ===
  [PASS] A2DP_001: audio sink connect/re-connect 
  [PASS] A2DP_002: stereo headset playback 
  [PASS] A2DP_003: stereo headset voice record 
  [PASS] A2DP_004: Codec support: SBC 
  [PASS] A2DP_005: Playback music on line
  [PASS] A2DP_006: SINK role, as speaker for other device music playing
    With some BT headset, we ever met issue [https://bugs.meego.com/show_bug.cgi?id=25480 Bug#25480 A2DP-source: pulseaudio fails to create bluez_sink and bluez_source]

=== Audio-AVRCP ===
  [PASS] AVRCP_001: Volume Up/Down
  [Block] AVRCP_002: Play/Pause (by unavailable media-player)
  [Block] AVRCP_003: Next/Privious (by unavailable media-player)
  [PASS] AVRCP_004: Connection Establish/Release
  [N/A] AVRCP_005: Audio metadata get.
    We still try to find avaliable media-player to support AVRCP-1.4

=== Audio-HFP ===
    Ofono needs to update its API, since BlueZ already took the changes. Here is bug [https://bugs.meego.com/show_bug.cgi?id=25748 Bug#25748 HFP: Ofono List-modems shows no modem after paired with phone]
  [FAIL] HFP_001: RFCOMM connection on AG
    Need install sbc for latest pulseaudio 
  [FAIL] HFP_002: list (ofono) hfp modem 
  [FAIL] HFP_003: HFP modem establishment (Ofono)
  [FAIL] HFP_004: Audio SNK and SRC establishment  
  [FAIL] HFP_005: voicecall, audio creates BT SNK/SRC 
  [FAIL] HFP_006: redirect AG SNK/SRC to local SRC/SNK
    BlueZ interface has some regression, bug is reported as [https://bugs.meego.com/show_bug.cgi?id=25473 Bug#25473 HFP: latest bluez cannot support pulseaudio to create bluez_sink and bluez_source]
    Pulseaudio has new crash bug: [https://bugs.meego.com/show_bug.cgi?id=25593 Bug#25593 Pulseaudio crashes after connect BT sink to alsa source]  
  [FAIL] HFP_007: Check AT-commands from HF part
  [FAIL] HFP_008: Connect BT Headset to HFP phone

=== Audio-HSP ===
  [PASS] HSP_001: Use mono headset to play music
  *** DUT is a Netbook, unable to take phone ***
  [N/A] HSP_002: Take incoming call by button-press
  [N/A] HSP_003: Audio transfer between AG and HS
  [PASS] HSP_004: Adjust Volume Up/Down

=== OBEX-FTP ===
  [PASS] FTP_001: pull/push files from/to server.
  [PASS] FTP_002: browse server files 
  [PASS] FTP_003: Client "delete file", "rename file"
  [PASS] FTP_004: Server enables FTP parameter  
  [PASS] FTP_005: Server sets sharing root path
  [PASS] FTP_006: Server handles all requirements
  [PASS] FTP_007: Server has ability to set permission for FTP
  [PASS] FTP_008: FTP data-rate about 30k/s~230k/s
  [PASS] FTP_009: Big size file transferred stable

=== OBEX-OPP ===
  [PASS] OPP_001: pull un-patterned object from server. 
  [PASS] OPP_002: Server enables OPP parameter
  [PASS] OPP_003: During object transferring, the progress is clear

=== OBEX-PBAP ===
  [PASS] PBAP_001: Client gets phone book entries from server
  [PASS] PBAP_002: Client gets ICH, OCH, MCH and CCH from server
  [FAIL] PBAP_003: PSE can provide PBAP daemon by enabling corresponding parameter
  Case failed due to [https://bugs.meego.com/show_bug.cgi?id=25598 Bug#25598, PBAP server fails to open /telecom/pb.vcf file]
  [PASS] PBAP_004: Both sides support vCard2.1/vCard3.0

=== OBEX-SYNC ===
  [PASS] SYNC_001: Server enables SYNC daemon
  [PASS] SYNC_002: During sync, Server can show "syncing..."
  [PASS] SYNC_003: client can set PIM fetching from INT
  [PASS] SYNC_004: Server get/put entire phonebook from/to client. 
  [PASS] SYNC_005: Client can support vCard2.1, vCard3.0

=== OBEX-MAP MCE ===
  [FAIL] MAP_001: MCE can browse message/folder list on MSE 
    Regression happened on client side, bug is reported as [https://bugs.meego.com/show_bug.cgi?id=25595 Bug#25595 MAP client fail to get message list from Samsung GT-i9100]
  [BLOCK] MAP_002: MCE can upload local message to MSE
  [BLOCK] MAP_003: MCE can delete the message on MSE side
  [BLOCK] MAP_004: MCE can take use of MSE to send message

=== Network-PAN ===
  [PASS] PAN_001: PANU can init nap0 device connect to NAP
  [PASS] PAN_002: PANU can get ip address or assigned static ip
  [PASS] PAN_003: PANU can logon internet website 
  [N/A] PAN_004: NAP can init bridge bnep 
  [N/A] PAN_005: NAP can support one or multiple PANU connection
  [N/A] PAN_006: NAP can have DHCP responding to each PANU
  Current connman does not support DHCP functions.

=== Network-BNEP ===
  [PASS] BNEP_001: Check BNEP support on DUT

=== Network-DUN ===
  [PASS] DUN_001: GW (DUT) can parse a series of AT commands from the data terminal. 
  [PASS] DUN_002: DT can build up rfcomm device by bluetooth and DUN modem by Ofono.
  [PASS] DUN_003: DT can use Ofono to dial up special service number ("*99#").
  [PASS] DUN_004: When network connected, DT can log on website in internet.

=== SIM-SAP ===
  The SAP test method is still in investigation. 
  [N/A] SAP_001: Server can enable a module to register Client
  [N/A] SAP_002: Server can power on/off SIM Card or reset it 
  [N/A] SAP_003: Server can disconnect Client  
  [N/A] SAP_004: Server can disconnect SIM
  [N/A] SAP_005: Client can connect to Server SIM
  [N/A] SAP_006: Client can power on Server SIM
  [N/A] SAP_007: Client can disconnect Server

=== Generic-GAP ===
  [PASS] GAP_001: PSCAN/ISCAN mode setting.
  [PASS] GAP_002: Active pairing to another bluetooth device.
  [PASS] GAP_003: Passive pairing, accepte pair master requirement
  [PASS] GAP_004: SSP supports 
  [PASS] GAP_005: If lost link-key, it needs re-pair 
  [PASS] GAP_006: reboot DUT with link-key restored, no need to re-pair 
  [PASS] GAP_007: Pair can be released

=== Generic-SDP ===
  [PASS] SDP_001: browse available service list in local
  [PASS] SDP_002: browse available service list from remote
  [PASS] SDP_003: browse service by RecHandle for detail info
  [PASS] SDP_004: Server can answer SDP searching request
  [PASS] SDP_005: Server can add/del/update service records

=== Others-HID ===
  [PASS] HID_001: Host can search nearby HID device.
  [PASS] HID_002: [BAT] Host can connect to HID device.
  [PASS] HID_003: Host can handle multiple human input/output devices.

=== HCI ===
  [PASS] HCI_001: Build hci connection between Dev_A and Dev_B.
  [PASS] HCI_002: Receive ACL data with HCI.
  [PASS] HCI_003: Send ACL data with HCI.
  [PASS] HCI_004: Change name of remote Dev by HCI.
  [PASS] HCI_005: Change class of remote Dev by HCI.
  [PASS] HCI_006: Read local HCI controller information.
  [PASS] HCI_007: Read remote HCI controller information.

=== L2CAP ===
  [PASS] L2CAP_001: Build l2cap connection between Dev_A and Dev_B.  
  [PASS] L2CAP_002: Dev_A and Dev_B take l2cap protocol to do pingpong.

=== RFCOMM ===
  [PASS] RFCOMM_001: Build rfcomm connection between Dev_A and Dev_B.  
  [PASS] RFCOMM_002: Dev_A and Dev_B take rfcomm protocol to do pingpong.

=== Settings ===
  [PASS] SET_001: [BAT] No error output during bluetoothd startup process.

=== Bluetooth Utils ===
Some basic checking for hciconfig, hcitool, sdptool commands. 
  [PASS] BTCMD_001: hciconfig -a
  [PASS] BTCMD_002: hciconfig <adapter> piscan
  [PASS] BTCMD_003: hciconfig <adapter> up/down  
  [PASS] BTCMD_004: hcitool scan
  [PASS] BTCMD_005: sdptool browse local
  [PASS] BTCMD_006: sdptool browse <ermote MAC>
  [PASS] BTCMD_007: sdptool add/del



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox