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

Commit 8eb200bd authored by Mat Martineau's avatar Mat Martineau Committed by Gustavo Padovan
Browse files

Bluetooth: Handle physical link completion



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: default avatarMat Martineau <mathewm@codeaurora.org>
Acked-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Acked-by: default avatarAndrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent 3fd71a0a
Loading
Loading
Loading
Loading
+164 −0
Original line number Original line Diff line number Diff line
@@ -1016,6 +1016,19 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
	l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
	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)
static void l2cap_move_setup(struct l2cap_chan *chan)
{
{
	struct sk_buff *skb;
	struct sk_buff *skb;
@@ -4187,6 +4200,25 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
	return 0;
	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);

	__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
}

static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
{
{
	struct l2cap_move_chan_rsp rsp;
	struct l2cap_move_chan_rsp rsp;
@@ -4363,6 +4395,138 @@ 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);
}

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, 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, 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, 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,
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)