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

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
...@@ -67,6 +67,8 @@ int hci_disable_advertising_sync(struct hci_dev *hdev); ...@@ -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_sync(struct hci_dev *hdev);
int hci_update_passive_scan(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_write_fast_connectable_sync(struct hci_dev *hdev, bool enable);
int hci_update_scan_sync(struct hci_dev *hdev); int hci_update_scan_sync(struct hci_dev *hdev);
......
...@@ -1309,6 +1309,25 @@ int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk, ...@@ -1309,6 +1309,25 @@ int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk,
return 0; 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) int hci_disable_advertising_sync(struct hci_dev *hdev)
{ {
u8 enable = 0x00; u8 enable = 0x00;
......
...@@ -5942,7 +5942,6 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, ...@@ -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_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_FAILED); MGMT_STATUS_FAILED);
if (cmd) if (cmd)
mgmt_pending_free(cmd); mgmt_pending_free(cmd);
} }
...@@ -6534,14 +6533,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, ...@@ -6534,14 +6533,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
return err; 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 hci_conn *conn = cmd->user_data;
struct mgmt_cp_get_conn_info *cp = cmd->param;
struct mgmt_rp_get_conn_info rp; 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) { if (status == MGMT_STATUS_SUCCESS) {
rp.rssi = conn->rssi; rp.rssi = conn->rssi;
rp.tx_power = conn->tx_power; rp.tx_power = conn->tx_power;
...@@ -6552,67 +6556,58 @@ static int conn_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status) ...@@ -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; rp.max_tx_power = HCI_TX_POWER_INVALID;
} }
err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
status, &rp, sizeof(rp)); &rp, sizeof(rp));
hci_conn_drop(conn); if (conn) {
hci_conn_put(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, static int get_conn_info_sync(struct hci_dev *hdev, void *data)
u16 opcode)
{ {
struct hci_cp_read_rssi *cp; struct mgmt_pending_cmd *cmd = data;
struct mgmt_pending_cmd *cmd; struct mgmt_cp_get_conn_info *cp = cmd->param;
struct hci_conn *conn; struct hci_conn *conn;
u16 handle; int err;
u8 status; __le16 handle;
bt_dev_dbg(hdev, "status 0x%02x", hci_status);
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 if (!conn || conn != cmd->user_data || conn->state != BT_CONNECTED) {
* Level so we check which one was last sent to retrieve connection if (cmd->user_data) {
* handle. Both commands have handle as first parameter so it's safe to hci_conn_drop(cmd->user_data);
* cast data on the same command struct. hci_conn_put(cmd->user_data);
* cmd->user_data = NULL;
* 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 return MGMT_STATUS_NOT_CONNECTED;
* 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 (!cp) { handle = cpu_to_le16(conn->handle);
bt_dev_err(hdev, "invalid sent_cmd in conn_info response");
goto unlock;
}
handle = __le16_to_cpu(cp->handle); /* Refresh RSSI each time */
conn = hci_conn_hash_lookup_handle(hdev, handle); err = hci_read_rssi_sync(hdev, handle);
if (!conn) {
bt_dev_err(hdev, "unknown handle (%u) in conn_info response",
handle);
goto unlock;
}
cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn); /* For LE links TX power does not change thus we don't need to
if (!cmd) * query for it once value is known.
goto unlock; */
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);
cmd->cmd_complete(cmd, status); /* Max TX power needs to be read only once per connection */
mgmt_pending_remove(cmd); if (!err && conn->max_tx_power == HCI_TX_POWER_INVALID)
err = hci_read_tx_power_sync(hdev, handle, 0x01);
unlock: return err;
hci_dev_unlock(hdev);
} }
static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, 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, ...@@ -6657,12 +6652,6 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock; 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 /* 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. * 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, ...@@ -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 + if (time_after(jiffies, conn->conn_info_timestamp +
msecs_to_jiffies(conn_info_age)) || msecs_to_jiffies(conn_info_age)) ||
!conn->conn_info_timestamp) { !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; struct mgmt_pending_cmd *cmd;
hci_req_init(&req, hdev); cmd = mgmt_pending_new(sk, MGMT_OP_GET_CONN_INFO, hdev, data,
req_rssi_cp.handle = cpu_to_le16(conn->handle); len);
hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp), if (!cmd)
&req_rssi_cp); err = -ENOMEM;
else
/* For LE links TX power does not change thus we don't need to err = hci_cmd_sync_queue(hdev, get_conn_info_sync,
* query for it once value is known. cmd, get_conn_info_complete);
*/
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);
}
/* Max TX power needs to be read only once per connection */ if (err < 0) {
if (conn->max_tx_power == HCI_TX_POWER_INVALID) { mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
req_txp_cp.handle = cpu_to_le16(conn->handle); MGMT_STATUS_FAILED, &rp, sizeof(rp));
req_txp_cp.type = 0x01;
hci_req_add(&req, HCI_OP_READ_TX_POWER,
sizeof(req_txp_cp), &req_txp_cp);
}
err = hci_req_run(&req, conn_info_refresh_complete); if (cmd)
if (err < 0) mgmt_pending_free(cmd);
goto unlock;
cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
data, len);
if (!cmd) {
err = -ENOMEM;
goto unlock; goto unlock;
} }
hci_conn_hold(conn); hci_conn_hold(conn);
cmd->user_data = hci_conn_get(conn); cmd->user_data = hci_conn_get(conn);
cmd->cmd_complete = conn_info_cmd_complete;
conn->conn_info_timestamp = jiffies; conn->conn_info_timestamp = jiffies;
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment