Commit 9713c17b authored by Marcel Holtmann's avatar Marcel Holtmann Committed by Johan Hedberg

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: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent d603b76b
...@@ -489,6 +489,12 @@ struct mgmt_cp_set_external_config { ...@@ -489,6 +489,12 @@ struct mgmt_cp_set_external_config {
} __packed; } __packed;
#define MGMT_SET_EXTERNAL_CONFIG_SIZE 1 #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 #define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete { struct mgmt_ev_cmd_complete {
__le16 opcode; __le16 opcode;
......
...@@ -2302,12 +2302,14 @@ static int hci_dev_do_open(struct hci_dev *hdev) ...@@ -2302,12 +2302,14 @@ static int hci_dev_do_open(struct hci_dev *hdev)
ret = __hci_unconf_init(hdev); ret = __hci_unconf_init(hdev);
} }
/* If public address change is configured, ensure that the if (test_bit(HCI_CONFIG, &hdev->dev_flags)) {
* address gets programmed. If the driver does not support /* If public address change is configured, ensure that
* changing the public address, fail the power on procedure. * the address gets programmed. If the driver does not
*/ * support changing the public address, fail the power
if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) { * on procedure.
if (hdev->set_bdaddr) */
if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
hdev->set_bdaddr)
ret = hdev->set_bdaddr(hdev, &hdev->public_addr); ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
else else
ret = -EADDRNOTAVAIL; ret = -EADDRNOTAVAIL;
......
...@@ -92,6 +92,7 @@ static const u16 mgmt_commands[] = { ...@@ -92,6 +92,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_OP_READ_UNCONF_INDEX_LIST,
MGMT_OP_READ_CONFIG_INFO, MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_SET_EXTERNAL_CONFIG, MGMT_OP_SET_EXTERNAL_CONFIG,
MGMT_OP_SET_PUBLIC_ADDRESS,
}; };
static const u16 mgmt_events[] = { static const u16 mgmt_events[] = {
...@@ -5459,6 +5460,58 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, ...@@ -5459,6 +5460,58 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev,
return err; 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 { static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len); u16 data_len);
...@@ -5522,6 +5575,7 @@ static const struct mgmt_handler { ...@@ -5522,6 +5575,7 @@ static const struct mgmt_handler {
{ read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE },
{ read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE },
{ set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_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) 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) ...@@ -5576,7 +5630,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
opcode != MGMT_OP_READ_CONFIG_INFO && 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, err = cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX); MGMT_STATUS_INVALID_INDEX);
goto done; goto done;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment