Commit 47db6b42 authored by Brian Gix's avatar Brian Gix Committed by Marcel Holtmann
Browse files

Bluetooth: hci_sync: Convert MGMT_OP_GET_CONN_INFO



Synchronous version of MGMT_OP_GET_CONN_INFO

Implements:
  hci_read_rssi_sync
  hci_read_tx_power_sync

Signed-off-by: default avatarBrian Gix <brian.gix@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 451d95a9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@ int hci_disable_advertising_sync(struct hci_dev *hdev);

int hci_update_passive_scan_sync(struct hci_dev *hdev);
int hci_update_passive_scan(struct hci_dev *hdev);
int hci_read_rssi_sync(struct hci_dev *hdev, __le16 handle);
int hci_read_tx_power_sync(struct hci_dev *hdev, __le16 handle, u8 type);

int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable);
int hci_update_scan_sync(struct hci_dev *hdev);
+19 −0
Original line number Diff line number Diff line
@@ -1309,6 +1309,25 @@ int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk,
	return 0;
}

int hci_read_rssi_sync(struct hci_dev *hdev, __le16 handle)
{
	struct hci_cp_read_rssi cp;

	cp.handle = handle;
	return __hci_cmd_sync_status(hdev, HCI_OP_READ_RSSI,
					sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_read_tx_power_sync(struct hci_dev *hdev, __le16 handle, u8 type)
{
	struct hci_cp_read_tx_power cp;

	cp.handle = handle;
	cp.type = type;
	return __hci_cmd_sync_status(hdev, HCI_OP_READ_TX_POWER,
					sizeof(cp), &cp, HCI_CMD_TIMEOUT);
}

int hci_disable_advertising_sync(struct hci_dev *hdev)
{
	u8 enable = 0x00;
+58 −90
Original line number Diff line number Diff line
@@ -5942,7 +5942,6 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
		mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
				MGMT_STATUS_FAILED);


		if (cmd)
			mgmt_pending_free(cmd);
	}
@@ -6534,14 +6533,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
	return err;
}

static int conn_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
{
	struct mgmt_pending_cmd *cmd = data;
	struct hci_conn *conn = cmd->user_data;
	struct mgmt_cp_get_conn_info *cp = cmd->param;
	struct mgmt_rp_get_conn_info rp;
	int err;
	u8 status;

	memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
	bt_dev_dbg(hdev, "err %d", err);

	memcpy(&rp.addr, &cp->addr.bdaddr, sizeof(rp.addr));

	status = mgmt_status(err);
	if (status == MGMT_STATUS_SUCCESS) {
		rp.rssi = conn->rssi;
		rp.tx_power = conn->tx_power;
@@ -6552,67 +6556,58 @@ static int conn_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
		rp.max_tx_power = HCI_TX_POWER_INVALID;
	}

	err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
				status, &rp, sizeof(rp));
	mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
			  &rp, sizeof(rp));

	if (conn) {
		hci_conn_drop(conn);
		hci_conn_put(conn);
	}

	return err;
	mgmt_pending_free(cmd);
}

static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status,
				       u16 opcode)
static int get_conn_info_sync(struct hci_dev *hdev, void *data)
{
	struct hci_cp_read_rssi *cp;
	struct mgmt_pending_cmd *cmd;
	struct mgmt_pending_cmd *cmd = data;
	struct mgmt_cp_get_conn_info *cp = cmd->param;
	struct hci_conn *conn;
	u16 handle;
	u8 status;

	bt_dev_dbg(hdev, "status 0x%02x", hci_status);
	int err;
	__le16   handle;

	hci_dev_lock(hdev);
	/* Make sure we are still connected */
	if (cp->addr.type == BDADDR_BREDR)
		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
					       &cp->addr.bdaddr);
	else
		conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);

	/* Commands sent in request are either Read RSSI or Read Transmit Power
	 * Level so we check which one was last sent to retrieve connection
	 * handle.  Both commands have handle as first parameter so it's safe to
	 * cast data on the same command struct.
	 *
	 * First command sent is always Read RSSI and we fail only if it fails.
	 * In other case we simply override error to indicate success as we
	 * already remembered if TX power value is actually valid.
	 */
	cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
	if (!cp) {
		cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
		status = MGMT_STATUS_SUCCESS;
	} else {
		status = mgmt_status(hci_status);
	if (!conn || conn != cmd->user_data || conn->state != BT_CONNECTED) {
		if (cmd->user_data) {
			hci_conn_drop(cmd->user_data);
			hci_conn_put(cmd->user_data);
			cmd->user_data = NULL;
		}

	if (!cp) {
		bt_dev_err(hdev, "invalid sent_cmd in conn_info response");
		goto unlock;
		return MGMT_STATUS_NOT_CONNECTED;
	}

	handle = __le16_to_cpu(cp->handle);
	conn = hci_conn_hash_lookup_handle(hdev, handle);
	if (!conn) {
		bt_dev_err(hdev, "unknown handle (%u) in conn_info response",
			   handle);
		goto unlock;
	}
	handle = cpu_to_le16(conn->handle);

	cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
	if (!cmd)
		goto unlock;
	/* Refresh RSSI each time */
	err = hci_read_rssi_sync(hdev, handle);

	cmd->cmd_complete(cmd, status);
	mgmt_pending_remove(cmd);
	/* For LE links TX power does not change thus we don't need to
	 * query for it once value is known.
	 */
	if (!err && (!bdaddr_type_is_le(cp->addr.type) ||
		     conn->tx_power == HCI_TX_POWER_INVALID))
		err = hci_read_tx_power_sync(hdev, handle, 0x00);

unlock:
	hci_dev_unlock(hdev);
	/* Max TX power needs to be read only once per connection */
	if (!err && conn->max_tx_power == HCI_TX_POWER_INVALID)
		err = hci_read_tx_power_sync(hdev, handle, 0x01);

	return err;
}

static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -6657,12 +6652,6 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
		goto unlock;
	}

	if (pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
					MGMT_STATUS_BUSY, &rp, sizeof(rp));
		goto unlock;
	}

	/* To avoid client trying to guess when to poll again for information we
	 * calculate conn info age as random value between min/max set in hdev.
	 */
@@ -6676,49 +6665,28 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
	if (time_after(jiffies, conn->conn_info_timestamp +
		       msecs_to_jiffies(conn_info_age)) ||
	    !conn->conn_info_timestamp) {
		struct hci_request req;
		struct hci_cp_read_tx_power req_txp_cp;
		struct hci_cp_read_rssi req_rssi_cp;
		struct mgmt_pending_cmd *cmd;

		hci_req_init(&req, hdev);
		req_rssi_cp.handle = cpu_to_le16(conn->handle);
		hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
			    &req_rssi_cp);

		/* For LE links TX power does not change thus we don't need to
		 * query for it once value is known.
		 */
		if (!bdaddr_type_is_le(cp->addr.type) ||
		    conn->tx_power == HCI_TX_POWER_INVALID) {
			req_txp_cp.handle = cpu_to_le16(conn->handle);
			req_txp_cp.type = 0x00;
			hci_req_add(&req, HCI_OP_READ_TX_POWER,
				    sizeof(req_txp_cp), &req_txp_cp);
		}
		cmd = mgmt_pending_new(sk, MGMT_OP_GET_CONN_INFO, hdev, data,
				       len);
		if (!cmd)
			err = -ENOMEM;
		else
			err = hci_cmd_sync_queue(hdev, get_conn_info_sync,
						 cmd, get_conn_info_complete);

		/* Max TX power needs to be read only once per connection */
		if (conn->max_tx_power == HCI_TX_POWER_INVALID) {
			req_txp_cp.handle = cpu_to_le16(conn->handle);
			req_txp_cp.type = 0x01;
			hci_req_add(&req, HCI_OP_READ_TX_POWER,
				    sizeof(req_txp_cp), &req_txp_cp);
		}
		if (err < 0) {
			mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
					  MGMT_STATUS_FAILED, &rp, sizeof(rp));

		err = hci_req_run(&req, conn_info_refresh_complete);
		if (err < 0)
			goto unlock;
			if (cmd)
				mgmt_pending_free(cmd);

		cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
				       data, len);
		if (!cmd) {
			err = -ENOMEM;
			goto unlock;
		}

		hci_conn_hold(conn);
		cmd->user_data = hci_conn_get(conn);
		cmd->cmd_complete = conn_info_cmd_complete;

		conn->conn_info_timestamp = jiffies;
	} else {