summaryrefslogtreecommitdiffstats
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
authorAndrzej Kaczmarek <andrzej.kaczmarek@tieto.com>2014-05-14 07:43:03 -0400
committerMarcel Holtmann <marcel@holtmann.org>2014-05-16 00:48:06 -0400
commitdd9838087b8c2b45c7976e46290749732d7af9d5 (patch)
tree0c2a6fbbed4ca96a7da57414b36fa191242abcfa /net/bluetooth/mgmt.c
parent31ad169148df2252a774c73c504aff43bfa4b656 (diff)
Bluetooth: Add support to get connection information
This patch adds support for Get Connection Information mgmt command which can be used to query for information about connection, i.e. RSSI and local TX power level. In general values cached in hci_conn are returned as long as they are considered valid, i.e. do not exceed age limit set in hdev. This limit is calculated as random value between min/max values to avoid client trying to guess when to poll for updated information. Signed-off-by: Andrzej Kaczmarek <andrzej.kaczmarek@tieto.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r--net/bluetooth/mgmt.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f2a9422d6139..0e5a316fafbf 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -83,6 +83,7 @@ static const u16 mgmt_commands[] = {
83 MGMT_OP_SET_DEBUG_KEYS, 83 MGMT_OP_SET_DEBUG_KEYS,
84 MGMT_OP_SET_PRIVACY, 84 MGMT_OP_SET_PRIVACY,
85 MGMT_OP_LOAD_IRKS, 85 MGMT_OP_LOAD_IRKS,
86 MGMT_OP_GET_CONN_INFO,
86}; 87};
87 88
88static const u16 mgmt_events[] = { 89static const u16 mgmt_events[] = {
@@ -4557,6 +4558,200 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
4557 return err; 4558 return err;
4558} 4559}
4559 4560
4561struct cmd_conn_lookup {
4562 struct hci_conn *conn;
4563 bool valid_tx_power;
4564 u8 mgmt_status;
4565};
4566
4567static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
4568{
4569 struct cmd_conn_lookup *match = data;
4570 struct mgmt_cp_get_conn_info *cp;
4571 struct mgmt_rp_get_conn_info rp;
4572 struct hci_conn *conn = cmd->user_data;
4573
4574 if (conn != match->conn)
4575 return;
4576
4577 cp = (struct mgmt_cp_get_conn_info *) cmd->param;
4578
4579 memset(&rp, 0, sizeof(rp));
4580 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
4581 rp.addr.type = cp->addr.type;
4582
4583 if (!match->mgmt_status) {
4584 rp.rssi = conn->rssi;
4585
4586 if (match->valid_tx_power)
4587 rp.tx_power = conn->tx_power;
4588 else
4589 rp.tx_power = HCI_TX_POWER_INVALID;
4590
4591 rp.max_tx_power = HCI_TX_POWER_INVALID;
4592 }
4593
4594 cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
4595 match->mgmt_status, &rp, sizeof(rp));
4596
4597 hci_conn_drop(conn);
4598
4599 mgmt_pending_remove(cmd);
4600}
4601
4602static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
4603{
4604 struct hci_cp_read_rssi *cp;
4605 struct hci_conn *conn;
4606 struct cmd_conn_lookup match;
4607 u16 handle;
4608
4609 BT_DBG("status 0x%02x", status);
4610
4611 hci_dev_lock(hdev);
4612
4613 /* TX power data is valid in case request completed successfully,
4614 * otherwise we assume it's not valid.
4615 */
4616 match.valid_tx_power = !status;
4617
4618 /* Commands sent in request are either Read RSSI or Read Transmit Power
4619 * Level so we check which one was last sent to retrieve connection
4620 * handle. Both commands have handle as first parameter so it's safe to
4621 * cast data on the same command struct.
4622 *
4623 * First command sent is always Read RSSI and we fail only if it fails.
4624 * In other case we simply override error to indicate success as we
4625 * already remembered if TX power value is actually valid.
4626 */
4627 cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
4628 if (!cp) {
4629 cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
4630 status = 0;
4631 }
4632
4633 if (!cp) {
4634 BT_ERR("invalid sent_cmd in response");
4635 goto unlock;
4636 }
4637
4638 handle = __le16_to_cpu(cp->handle);
4639 conn = hci_conn_hash_lookup_handle(hdev, handle);
4640 if (!conn) {
4641 BT_ERR("unknown handle (%d) in response", handle);
4642 goto unlock;
4643 }
4644
4645 match.conn = conn;
4646 match.mgmt_status = mgmt_status(status);
4647
4648 /* Cache refresh is complete, now reply for mgmt request for given
4649 * connection only.
4650 */
4651 mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
4652 get_conn_info_complete, &match);
4653
4654unlock:
4655 hci_dev_unlock(hdev);
4656}
4657
4658static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
4659 u16 len)
4660{
4661 struct mgmt_cp_get_conn_info *cp = data;
4662 struct mgmt_rp_get_conn_info rp;
4663 struct hci_conn *conn;
4664 unsigned long conn_info_age;
4665 int err = 0;
4666
4667 BT_DBG("%s", hdev->name);
4668
4669 memset(&rp, 0, sizeof(rp));
4670 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
4671 rp.addr.type = cp->addr.type;
4672
4673 if (!bdaddr_type_is_valid(cp->addr.type))
4674 return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
4675 MGMT_STATUS_INVALID_PARAMS,
4676 &rp, sizeof(rp));
4677
4678 hci_dev_lock(hdev);
4679
4680 if (!hdev_is_powered(hdev)) {
4681 err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
4682 MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
4683 goto unlock;
4684 }
4685
4686 if (cp->addr.type == BDADDR_BREDR)
4687 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
4688 &cp->addr.bdaddr);
4689 else
4690 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
4691
4692 if (!conn || conn->state != BT_CONNECTED) {
4693 err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
4694 MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
4695 goto unlock;
4696 }
4697
4698 /* To avoid client trying to guess when to poll again for information we
4699 * calculate conn info age as random value between min/max set in hdev.
4700 */
4701 conn_info_age = hdev->conn_info_min_age +
4702 prandom_u32_max(hdev->conn_info_max_age -
4703 hdev->conn_info_min_age);
4704
4705 /* Query controller to refresh cached values if they are too old or were
4706 * never read.
4707 */
4708 if (time_after(jiffies, conn->conn_info_timestamp + conn_info_age) ||
4709 !conn->conn_info_timestamp) {
4710 struct hci_request req;
4711 struct hci_cp_read_tx_power req_txp_cp;
4712 struct hci_cp_read_rssi req_rssi_cp;
4713 struct pending_cmd *cmd;
4714
4715 hci_req_init(&req, hdev);
4716 req_rssi_cp.handle = cpu_to_le16(conn->handle);
4717 hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
4718 &req_rssi_cp);
4719
4720 req_txp_cp.handle = cpu_to_le16(conn->handle);
4721 req_txp_cp.type = 0x00;
4722 hci_req_add(&req, HCI_OP_READ_TX_POWER,
4723 sizeof(req_txp_cp), &req_txp_cp);
4724
4725 err = hci_req_run(&req, conn_info_refresh_complete);
4726 if (err < 0)
4727 goto unlock;
4728
4729 cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
4730 data, len);
4731 if (!cmd) {
4732 err = -ENOMEM;
4733 goto unlock;
4734 }
4735
4736 hci_conn_hold(conn);
4737 cmd->user_data = conn;
4738
4739 conn->conn_info_timestamp = jiffies;
4740 } else {
4741 /* Cache is valid, just reply with values cached in hci_conn */
4742 rp.rssi = conn->rssi;
4743 rp.tx_power = conn->tx_power;
4744 rp.max_tx_power = HCI_TX_POWER_INVALID;
4745
4746 err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
4747 MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
4748 }
4749
4750unlock:
4751 hci_dev_unlock(hdev);
4752 return err;
4753}
4754
4560static const struct mgmt_handler { 4755static const struct mgmt_handler {
4561 int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, 4756 int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
4562 u16 data_len); 4757 u16 data_len);
@@ -4612,6 +4807,7 @@ static const struct mgmt_handler {
4612 { set_debug_keys, false, MGMT_SETTING_SIZE }, 4807 { set_debug_keys, false, MGMT_SETTING_SIZE },
4613 { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, 4808 { set_privacy, false, MGMT_SET_PRIVACY_SIZE },
4614 { load_irks, true, MGMT_LOAD_IRKS_SIZE }, 4809 { load_irks, true, MGMT_LOAD_IRKS_SIZE },
4810 { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE },
4615}; 4811};
4616 4812
4617 4813