Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b155ef9 authored by Mat Martineau's avatar Mat Martineau Committed by Gustavo Padovan
Browse files

Bluetooth: Move channel response



The move response command includes a result code indicating
"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: default avatarMat Martineau <mathewm@codeaurora.org>
Acked-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent 168df8e5
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,8 @@
#define L2CAP_ENC_TIMEOUT		msecs_to_jiffies(5000)
#define L2CAP_ENC_TIMEOUT		msecs_to_jiffies(5000)
#define L2CAP_CONN_TIMEOUT		msecs_to_jiffies(40000)
#define L2CAP_CONN_TIMEOUT		msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT		msecs_to_jiffies(4000)
#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
#define L2CAP_A2MP_DEFAULT_MTU		670


+168 −15
Original line number Original line Diff line number Diff line
@@ -129,6 +129,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
	return NULL;
	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)
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{
{
	struct l2cap_chan *c;
	struct l2cap_chan *c;
@@ -4185,23 +4199,34 @@ static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
	l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
	l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
}
}


static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn,
static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result)
				     struct l2cap_chan *chan,
				     u16 icid, u16 result)
{
{
	struct l2cap_move_chan_cfm cfm;
	struct l2cap_move_chan_cfm cfm;
	u8 ident;


	BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
	BT_DBG("chan %p, result 0x%4.4x", chan, result);


	ident = l2cap_get_ident(conn);
	chan->ident = l2cap_get_ident(chan->conn);
	if (chan)
		chan->ident = ident;


	cfm.icid = cpu_to_le16(icid);
	cfm.icid = cpu_to_le16(chan->scid);
	cfm.result = cpu_to_le16(result);
	cfm.result = cpu_to_le16(result);


	l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm);
	l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM,
		       sizeof(cfm), &cfm);

	__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
}

static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
{
	struct l2cap_move_chan_cfm cfm;

	BT_DBG("conn %p, icid 0x%4.4x", conn, icid);

	cfm.icid = cpu_to_le16(icid);
	cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED);

	l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
		       sizeof(cfm), &cfm);
}
}


static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
@@ -4223,6 +4248,13 @@ static void __release_logical_link(struct l2cap_chan *chan)
	/* Placeholder - release the logical link */
	/* Placeholder - release the logical link */
}
}


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,
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
					 struct l2cap_cmd_hdr *cmd,
					 struct l2cap_cmd_hdr *cmd,
					 u16 cmd_len, void *data)
					 u16 cmd_len, void *data)
@@ -4317,7 +4349,126 @@ send_move_response:
	return 0;
	return 0;
}
}


static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
{
	struct l2cap_chan *chan;
	struct hci_chan *hchan = NULL;

	chan = l2cap_get_chan_by_scid(conn, icid);
	if (!chan) {
		l2cap_send_move_chan_cfm_icid(conn, icid);
		return;
	}

	__clear_chan_timer(chan);
	if (result == L2CAP_MR_PEND)
		__set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);

	switch (chan->move_state) {
	case L2CAP_MOVE_WAIT_LOGICAL_COMP:
		/* Move confirm will be sent when logical link
		 * is complete.
		 */
		chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
		break;
	case L2CAP_MOVE_WAIT_RSP_SUCCESS:
		if (result == L2CAP_MR_PEND) {
			break;
		} 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(chan, L2CAP_MC_CONFIRMED);
		}
		break;
	case L2CAP_MOVE_WAIT_RSP:
		/* 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(chan, L2CAP_MC_UNCONFIRMED);
			break;
		}

		/* If the logical link is not yet connected, do not
		 * send confirmation.
		 */
		if (hchan->state != BT_CONNECTED)
			break;

		/* 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(chan, 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);
		break;
	default:
		/* Any other amp move state means the move failed. */
		chan->move_id = chan->local_amp_id;
		l2cap_move_done(chan);
		l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
	}

	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_icid(conn, icid);
		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;
			l2cap_move_done(chan);
		}
	}

	l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);

	l2cap_chan_unlock(chan);
}

static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
				  struct l2cap_cmd_hdr *cmd,
				  struct l2cap_cmd_hdr *cmd,
				  u16 cmd_len, void *data)
				  u16 cmd_len, void *data)
{
{
@@ -4332,8 +4483,10 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,


	BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
	BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);


	/* Placeholder: Always unconfirmed */
	if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
	l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
		l2cap_move_continue(conn, icid, result);
	else
		l2cap_move_fail(conn, cmd->ident, icid, result);


	return 0;
	return 0;
}
}