diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2014-06-28 10:54:07 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-07-03 11:42:49 -0400 |
commit | 958684263d3efbc721fb2b86f94876893eb638d2 (patch) | |
tree | cf49339ab50f18e351b1cd1dcdb83d516f5054ff /net | |
parent | 33f35721030185a2c5a1bb8afd4c3744709745b5 (diff) |
Bluetooth: Add support for Get Clock Info mgmt command
This patch implements support for the Get Clock Information mgmt
command. This is done by performing one or two HCI_Read_Clock commands
and creating the response from the stored values in the hci_dev and
hci_conn structs.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/mgmt.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 727ae15f9c36..6faa4616cbfe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -85,6 +85,7 @@ static const u16 mgmt_commands[] = { | |||
85 | MGMT_OP_SET_PRIVACY, | 85 | MGMT_OP_SET_PRIVACY, |
86 | MGMT_OP_LOAD_IRKS, | 86 | MGMT_OP_LOAD_IRKS, |
87 | MGMT_OP_GET_CONN_INFO, | 87 | MGMT_OP_GET_CONN_INFO, |
88 | MGMT_OP_GET_CLOCK_INFO, | ||
88 | }; | 89 | }; |
89 | 90 | ||
90 | static const u16 mgmt_events[] = { | 91 | static const u16 mgmt_events[] = { |
@@ -571,6 +572,22 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) | |||
571 | return NULL; | 572 | return NULL; |
572 | } | 573 | } |
573 | 574 | ||
575 | static struct pending_cmd *mgmt_pending_find_data(u16 opcode, | ||
576 | struct hci_dev *hdev, | ||
577 | const void *data) | ||
578 | { | ||
579 | struct pending_cmd *cmd; | ||
580 | |||
581 | list_for_each_entry(cmd, &hdev->mgmt_pending, list) { | ||
582 | if (cmd->user_data != data) | ||
583 | continue; | ||
584 | if (cmd->opcode == opcode) | ||
585 | return cmd; | ||
586 | } | ||
587 | |||
588 | return NULL; | ||
589 | } | ||
590 | |||
574 | static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) | 591 | static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) |
575 | { | 592 | { |
576 | u8 ad_len = 0; | 593 | u8 ad_len = 0; |
@@ -4820,6 +4837,132 @@ unlock: | |||
4820 | return err; | 4837 | return err; |
4821 | } | 4838 | } |
4822 | 4839 | ||
4840 | static void get_clock_info_complete(struct hci_dev *hdev, u8 status) | ||
4841 | { | ||
4842 | struct mgmt_cp_get_clock_info *cp; | ||
4843 | struct mgmt_rp_get_clock_info rp; | ||
4844 | struct hci_cp_read_clock *hci_cp; | ||
4845 | struct pending_cmd *cmd; | ||
4846 | struct hci_conn *conn; | ||
4847 | |||
4848 | BT_DBG("%s status %u", hdev->name, status); | ||
4849 | |||
4850 | hci_dev_lock(hdev); | ||
4851 | |||
4852 | hci_cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); | ||
4853 | if (!hci_cp) | ||
4854 | goto unlock; | ||
4855 | |||
4856 | if (hci_cp->which) { | ||
4857 | u16 handle = __le16_to_cpu(hci_cp->handle); | ||
4858 | conn = hci_conn_hash_lookup_handle(hdev, handle); | ||
4859 | } else { | ||
4860 | conn = NULL; | ||
4861 | } | ||
4862 | |||
4863 | cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); | ||
4864 | if (!cmd) | ||
4865 | goto unlock; | ||
4866 | |||
4867 | cp = cmd->param; | ||
4868 | |||
4869 | memset(&rp, 0, sizeof(rp)); | ||
4870 | memcpy(&rp.addr, &cp->addr, sizeof(rp.addr)); | ||
4871 | |||
4872 | if (status) | ||
4873 | goto send_rsp; | ||
4874 | |||
4875 | rp.local_clock = cpu_to_le32(hdev->clock); | ||
4876 | |||
4877 | if (conn) { | ||
4878 | rp.piconet_clock = cpu_to_le32(conn->clock); | ||
4879 | rp.accuracy = cpu_to_le16(conn->clock_accuracy); | ||
4880 | } | ||
4881 | |||
4882 | send_rsp: | ||
4883 | cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status), | ||
4884 | &rp, sizeof(rp)); | ||
4885 | mgmt_pending_remove(cmd); | ||
4886 | if (conn) | ||
4887 | hci_conn_drop(conn); | ||
4888 | |||
4889 | unlock: | ||
4890 | hci_dev_unlock(hdev); | ||
4891 | } | ||
4892 | |||
4893 | static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, | ||
4894 | u16 len) | ||
4895 | { | ||
4896 | struct mgmt_cp_get_clock_info *cp = data; | ||
4897 | struct mgmt_rp_get_clock_info rp; | ||
4898 | struct hci_cp_read_clock hci_cp; | ||
4899 | struct pending_cmd *cmd; | ||
4900 | struct hci_request req; | ||
4901 | struct hci_conn *conn; | ||
4902 | int err; | ||
4903 | |||
4904 | BT_DBG("%s", hdev->name); | ||
4905 | |||
4906 | memset(&rp, 0, sizeof(rp)); | ||
4907 | bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); | ||
4908 | rp.addr.type = cp->addr.type; | ||
4909 | |||
4910 | if (cp->addr.type != BDADDR_BREDR) | ||
4911 | return cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, | ||
4912 | MGMT_STATUS_INVALID_PARAMS, | ||
4913 | &rp, sizeof(rp)); | ||
4914 | |||
4915 | hci_dev_lock(hdev); | ||
4916 | |||
4917 | if (!hdev_is_powered(hdev)) { | ||
4918 | err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, | ||
4919 | MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); | ||
4920 | goto unlock; | ||
4921 | } | ||
4922 | |||
4923 | if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { | ||
4924 | conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, | ||
4925 | &cp->addr.bdaddr); | ||
4926 | if (!conn || conn->state != BT_CONNECTED) { | ||
4927 | err = cmd_complete(sk, hdev->id, | ||
4928 | MGMT_OP_GET_CLOCK_INFO, | ||
4929 | MGMT_STATUS_NOT_CONNECTED, | ||
4930 | &rp, sizeof(rp)); | ||
4931 | goto unlock; | ||
4932 | } | ||
4933 | } else { | ||
4934 | conn = NULL; | ||
4935 | } | ||
4936 | |||
4937 | cmd = mgmt_pending_add(sk, MGMT_OP_GET_CLOCK_INFO, hdev, data, len); | ||
4938 | if (!cmd) { | ||
4939 | err = -ENOMEM; | ||
4940 | goto unlock; | ||
4941 | } | ||
4942 | |||
4943 | hci_req_init(&req, hdev); | ||
4944 | |||
4945 | memset(&hci_cp, 0, sizeof(hci_cp)); | ||
4946 | hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); | ||
4947 | |||
4948 | if (conn) { | ||
4949 | hci_conn_hold(conn); | ||
4950 | cmd->user_data = conn; | ||
4951 | |||
4952 | hci_cp.handle = cpu_to_le16(conn->handle); | ||
4953 | hci_cp.which = 0x01; /* Piconet clock */ | ||
4954 | hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); | ||
4955 | } | ||
4956 | |||
4957 | err = hci_req_run(&req, get_clock_info_complete); | ||
4958 | if (err < 0) | ||
4959 | mgmt_pending_remove(cmd); | ||
4960 | |||
4961 | unlock: | ||
4962 | hci_dev_unlock(hdev); | ||
4963 | return err; | ||
4964 | } | ||
4965 | |||
4823 | static const struct mgmt_handler { | 4966 | static const struct mgmt_handler { |
4824 | int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, | 4967 | int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, |
4825 | u16 data_len); | 4968 | u16 data_len); |
@@ -4876,6 +5019,7 @@ static const struct mgmt_handler { | |||
4876 | { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, | 5019 | { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, |
4877 | { load_irks, true, MGMT_LOAD_IRKS_SIZE }, | 5020 | { load_irks, true, MGMT_LOAD_IRKS_SIZE }, |
4878 | { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, | 5021 | { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, |
5022 | { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, | ||
4879 | }; | 5023 | }; |
4880 | 5024 | ||
4881 | 5025 | ||