aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2014-06-28 10:54:07 -0400
committerMarcel Holtmann <marcel@holtmann.org>2014-07-03 11:42:49 -0400
commit958684263d3efbc721fb2b86f94876893eb638d2 (patch)
treecf49339ab50f18e351b1cd1dcdb83d516f5054ff /net
parent33f35721030185a2c5a1bb8afd4c3744709745b5 (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.c144
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
90static const u16 mgmt_events[] = { 91static 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
575static 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
574static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) 591static 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
4840static 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
4882send_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
4889unlock:
4890 hci_dev_unlock(hdev);
4891}
4892
4893static 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
4961unlock:
4962 hci_dev_unlock(hdev);
4963 return err;
4964}
4965
4823static const struct mgmt_handler { 4966static 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