Bluetooth: Add add/remove_remote_oob_data management commands
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / bluetooth / mgmt.c
index 0054c74e27b7fcd4971477408721b003e773f37a..a42dc8ca0a6f6b36afd92d57bf9d44c77b5b192f 100644 (file)
@@ -36,7 +36,7 @@ struct pending_cmd {
        struct list_head list;
        __u16 opcode;
        int index;
-       void *cmd;
+       void *param;
        struct sock *sk;
        void *user_data;
 };
@@ -183,6 +183,8 @@ static int read_controller_info(struct sock *sk, u16 index)
 
        set_bit(HCI_MGMT, &hdev->flags);
 
+       memset(&rp, 0, sizeof(rp));
+
        rp.type = hdev->dev_type;
 
        rp.powered = test_bit(HCI_UP, &hdev->flags);
@@ -204,6 +206,8 @@ static int read_controller_info(struct sock *sk, u16 index)
        rp.hci_ver = hdev->hci_ver;
        put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);
 
+       memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
+
        hci_dev_unlock_bh(hdev);
        hci_dev_put(hdev);
 
@@ -213,7 +217,7 @@ static int read_controller_info(struct sock *sk, u16 index)
 static void mgmt_pending_free(struct pending_cmd *cmd)
 {
        sock_put(cmd->sk);
-       kfree(cmd->cmd);
+       kfree(cmd->param);
        kfree(cmd);
 }
 
@@ -229,13 +233,14 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
        cmd->opcode = opcode;
        cmd->index = index;
 
-       cmd->cmd = kmalloc(len, GFP_ATOMIC);
-       if (!cmd->cmd) {
+       cmd->param = kmalloc(len, GFP_ATOMIC);
+       if (!cmd->param) {
                kfree(cmd);
                return NULL;
        }
 
-       memcpy(cmd->cmd, data, len);
+       if (data)
+               memcpy(cmd->param, data, len);
 
        cmd->sk = sk;
        sock_hold(sk);
@@ -1252,6 +1257,162 @@ failed:
        return err;
 }
 
+static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
+                                                               u16 len)
+{
+       struct mgmt_cp_set_local_name *mgmt_cp = (void *) data;
+       struct hci_cp_write_local_name hci_cp;
+       struct hci_dev *hdev;
+       struct pending_cmd *cmd;
+       int err;
+
+       BT_DBG("");
+
+       if (len != sizeof(*mgmt_cp))
+               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto failed;
+       }
+
+       memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
+       err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
+                                                               &hci_cp);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+failed:
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
+static int read_local_oob_data(struct sock *sk, u16 index)
+{
+       struct hci_dev *hdev;
+       struct pending_cmd *cmd;
+       int err;
+
+       BT_DBG("hci%u", index);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                                       ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       if (!test_bit(HCI_UP, &hdev->flags)) {
+               err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                               ENETDOWN);
+               goto unlock;
+       }
+
+       if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
+               err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                               EOPNOTSUPP);
+               goto unlock;
+       }
+
+       if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) {
+               err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
+               goto unlock;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
+static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
+                                                                       u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
+       int err;
+
+       BT_DBG("hci%u ", index);
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                                                       EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                                                       ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
+                                                               cp->randomizer);
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
+                                                                       0);
+
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
+static int remove_remote_oob_data(struct sock *sk, u16 index,
+                                               unsigned char *data, u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
+       int err;
+
+       BT_DBG("hci%u ", index);
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                               NULL, 0);
+
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
 {
        unsigned char *buf;
@@ -1347,6 +1508,20 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        case MGMT_OP_USER_CONFIRM_NEG_REPLY:
                err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
                break;
+       case MGMT_OP_SET_LOCAL_NAME:
+               err = set_local_name(sk, index, buf + sizeof(*hdr), len);
+               break;
+       case MGMT_OP_READ_LOCAL_OOB_DATA:
+               err = read_local_oob_data(sk, index);
+               break;
+       case MGMT_OP_ADD_REMOTE_OOB_DATA:
+               err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
+               break;
+       case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+               err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
+                                                                       len);
+               break;
+
        default:
                BT_DBG("Unknown op %u", opcode);
                err = cmd_status(sk, index, opcode, 0x01);
@@ -1380,7 +1555,7 @@ struct cmd_lookup {
 
 static void mode_rsp(struct pending_cmd *cmd, void *data)
 {
-       struct mgmt_mode *cp = cmd->cmd;
+       struct mgmt_mode *cp = cmd->param;
        struct cmd_lookup *match = data;
 
        if (cp->val != match->val)
@@ -1479,7 +1654,7 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr)
 
 static void disconnect_rsp(struct pending_cmd *cmd, void *data)
 {
-       struct mgmt_cp_disconnect *cp = cmd->cmd;
+       struct mgmt_cp_disconnect *cp = cmd->param;
        struct sock **sk = data;
        struct mgmt_rp_disconnect rp;
 
@@ -1643,3 +1818,66 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status)
 
        return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL);
 }
+
+int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status)
+{
+       struct pending_cmd *cmd;
+       struct mgmt_cp_set_local_name ev;
+       int err;
+
+       memset(&ev, 0, sizeof(ev));
+       memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+
+       cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index);
+       if (!cmd)
+               goto send_event;
+
+       if (status) {
+               err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO);
+               goto failed;
+       }
+
+       err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev,
+                                                               sizeof(ev));
+       if (err < 0)
+               goto failed;
+
+send_event:
+       err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev),
+                                                       cmd ? cmd->sk : NULL);
+
+failed:
+       if (cmd)
+               mgmt_pending_remove(cmd);
+       return err;
+}
+
+int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
+                                                               u8 status)
+{
+       struct pending_cmd *cmd;
+       int err;
+
+       BT_DBG("hci%u status %u", index, status);
+
+       cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index);
+       if (!cmd)
+               return -ENOENT;
+
+       if (status) {
+               err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                                       EIO);
+       } else {
+               struct mgmt_rp_read_local_oob_data rp;
+
+               memcpy(rp.hash, hash, sizeof(rp.hash));
+               memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
+
+               err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
+                                                       &rp, sizeof(rp));
+       }
+
+       mgmt_pending_remove(cmd);
+
+       return err;
+}