Bluetooth: Add support for Set Privacy command
authorJohan Hedberg <johan.hedberg@intel.com>
Sun, 23 Feb 2014 17:42:27 +0000 (19:42 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Sun, 23 Feb 2014 20:24:26 +0000 (12:24 -0800)
This patch adds support for handling the Set Privacy mgmt command,
including copying the value to hdev->irk and toggling the HCI_PRIVACY
flag.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/mgmt.c

index 49d52a3..37305fa 100644 (file)
@@ -81,6 +81,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_SCAN_PARAMS,
        MGMT_OP_SET_SECURE_CONN,
        MGMT_OP_SET_DEBUG_KEYS,
+       MGMT_OP_SET_PRIVACY,
        MGMT_OP_LOAD_IRKS,
 };
 
@@ -4227,6 +4228,51 @@ unlock:
        return err;
 }
 
+static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
+                      u16 len)
+{
+       struct mgmt_cp_set_privacy *cp = cp_data;
+       bool changed;
+       int err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       if (cp->privacy != 0x00 && cp->privacy != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
+                                 MGMT_STATUS_REJECTED);
+
+       hci_dev_lock(hdev);
+
+       if (cp->privacy) {
+               changed = !test_and_set_bit(HCI_PRIVACY, &hdev->dev_flags);
+               memcpy(hdev->irk, cp->irk, sizeof(hdev->irk));
+               set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+       } else {
+               changed = test_and_clear_bit(HCI_PRIVACY, &hdev->dev_flags);
+               memset(hdev->irk, 0, sizeof(hdev->irk));
+               clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
+       }
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (changed)
+               err = new_settings(hdev, sk);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static bool irk_is_valid(struct mgmt_irk_info *irk)
 {
        switch (irk->addr.type) {
@@ -4441,7 +4487,7 @@ static const struct mgmt_handler {
        { set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
        { set_secure_conn,        false, MGMT_SETTING_SIZE },
        { set_debug_keys,         false, MGMT_SETTING_SIZE },
-       { },
+       { set_privacy,            false, MGMT_SET_PRIVACY_SIZE },
        { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
 };