summaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2014-07-06 12:11:15 +0200
committerJohan Hedberg <johan.hedberg@intel.com>2014-07-06 12:42:20 +0200
commit9713c17b086c1ebfe34ea4d34147a778276e2dab (patch)
tree2e5dcc1d74effed93b919b8b3c617e937d7292e5 /net/bluetooth
parentBluetooth: Run controller setup after external configuration (diff)
downloadlinux-9713c17b086c1ebfe34ea4d34147a778276e2dab.tar.xz
linux-9713c17b086c1ebfe34ea4d34147a778276e2dab.zip
Bluetooth: Add support for changing the public device address
This adds support for changing the public device address. This feature is required by controllers that do not provide a public address and have HCI_QUIRK_INVALID_BDADDR set. Even if a controller has a public device address, this is useful when an embedded system wants to use its own value. As long as the driver provides the set_bdaddr callback, this allows changing the device address before powering on the controller. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_core.c14
-rw-r--r--net/bluetooth/mgmt.c57
2 files changed, 64 insertions, 7 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index bf7cf512df88..c96b96ca41ac 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2302,12 +2302,14 @@ static int hci_dev_do_open(struct hci_dev *hdev)
ret = __hci_unconf_init(hdev);
}
- /* If public address change is configured, ensure that the
- * address gets programmed. If the driver does not support
- * changing the public address, fail the power on procedure.
- */
- if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) {
- if (hdev->set_bdaddr)
+ if (test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+ /* If public address change is configured, ensure that
+ * the address gets programmed. If the driver does not
+ * support changing the public address, fail the power
+ * on procedure.
+ */
+ if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
+ hdev->set_bdaddr)
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
else
ret = -EADDRNOTAVAIL;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index d66463a52280..8275316ae099 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -92,6 +92,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_UNCONF_INDEX_LIST,
MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_SET_EXTERNAL_CONFIG,
+ MGMT_OP_SET_PUBLIC_ADDRESS,
};
static const u16 mgmt_events[] = {
@@ -5459,6 +5460,58 @@ unlock:
return err;
}
+static int set_public_address(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_public_address *cp = data;
+ bool changed;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+ MGMT_STATUS_REJECTED);
+
+ if (!bacmp(&cp->bdaddr, BDADDR_ANY))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ if (!hdev->set_bdaddr)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ hci_dev_lock(hdev);
+
+ changed = !!bacmp(&hdev->public_addr, &cp->bdaddr);
+ bacpy(&hdev->public_addr, &cp->bdaddr);
+
+ err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (!changed)
+ goto unlock;
+
+ if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+ err = new_options(hdev, sk);
+
+ if (is_configured(hdev)) {
+ mgmt_index_removed(hdev);
+
+ clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+
+ set_bit(HCI_CONFIG, &hdev->dev_flags);
+ set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+
+ queue_work(hdev->req_workqueue, &hdev->power_on);
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -5522,6 +5575,7 @@ static const struct mgmt_handler {
{ read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE },
{ read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE },
{ set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
+ { set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
};
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
@@ -5576,7 +5630,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
opcode != MGMT_OP_READ_CONFIG_INFO &&
- opcode != MGMT_OP_SET_EXTERNAL_CONFIG) {
+ opcode != MGMT_OP_SET_EXTERNAL_CONFIG &&
+ opcode != MGMT_OP_SET_PUBLIC_ADDRESS) {
err = cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;