diff options
-rw-r--r-- | include/net/bluetooth/hci_core.h | 2 | ||||
-rw-r--r-- | include/net/bluetooth/mgmt.h | 12 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 196 |
3 files changed, 210 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4623f45c8892..cbbab6327621 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
@@ -384,6 +384,8 @@ struct hci_conn { | |||
384 | __s8 tx_power; | 384 | __s8 tx_power; |
385 | unsigned long flags; | 385 | unsigned long flags; |
386 | 386 | ||
387 | unsigned long conn_info_timestamp; | ||
388 | |||
387 | __u8 remote_cap; | 389 | __u8 remote_cap; |
388 | __u8 remote_auth; | 390 | __u8 remote_auth; |
389 | __u8 remote_id; | 391 | __u8 remote_id; |
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d4b571c2f9fd..226ae03cafe7 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h | |||
@@ -409,6 +409,18 @@ struct mgmt_cp_load_irks { | |||
409 | } __packed; | 409 | } __packed; |
410 | #define MGMT_LOAD_IRKS_SIZE 2 | 410 | #define MGMT_LOAD_IRKS_SIZE 2 |
411 | 411 | ||
412 | #define MGMT_OP_GET_CONN_INFO 0x0031 | ||
413 | struct mgmt_cp_get_conn_info { | ||
414 | struct mgmt_addr_info addr; | ||
415 | } __packed; | ||
416 | #define MGMT_GET_CONN_INFO_SIZE MGMT_ADDR_INFO_SIZE | ||
417 | struct mgmt_rp_get_conn_info { | ||
418 | struct mgmt_addr_info addr; | ||
419 | __s8 rssi; | ||
420 | __s8 tx_power; | ||
421 | __s8 max_tx_power; | ||
422 | } __packed; | ||
423 | |||
412 | #define MGMT_EV_CMD_COMPLETE 0x0001 | 424 | #define MGMT_EV_CMD_COMPLETE 0x0001 |
413 | struct mgmt_ev_cmd_complete { | 425 | struct mgmt_ev_cmd_complete { |
414 | __le16 opcode; | 426 | __le16 opcode; |
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 | ||
88 | static const u16 mgmt_events[] = { | 89 | static 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 | ||
4561 | struct cmd_conn_lookup { | ||
4562 | struct hci_conn *conn; | ||
4563 | bool valid_tx_power; | ||
4564 | u8 mgmt_status; | ||
4565 | }; | ||
4566 | |||
4567 | static 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 | |||
4602 | static 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 | |||
4654 | unlock: | ||
4655 | hci_dev_unlock(hdev); | ||
4656 | } | ||
4657 | |||
4658 | static 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 | |||
4750 | unlock: | ||
4751 | hci_dev_unlock(hdev); | ||
4752 | return err; | ||
4753 | } | ||
4754 | |||
4560 | static const struct mgmt_handler { | 4755 | static 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 | ||