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>
This commit is contained in:
Marcel Holtmann 2014-07-06 12:11:15 +02:00 committed by Johan Hedberg
parent d603b76b0c
commit 9713c17b08
3 changed files with 70 additions and 7 deletions

View File

@ -489,6 +489,12 @@ struct mgmt_cp_set_external_config {
} __packed;
#define MGMT_SET_EXTERNAL_CONFIG_SIZE 1
#define MGMT_OP_SET_PUBLIC_ADDRESS 0x0039
struct mgmt_cp_set_public_address {
bdaddr_t bdaddr;
} __packed;
#define MGMT_SET_PUBLIC_ADDRESS_SIZE 6
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;

View File

@ -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;

View File

@ -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;