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:
Родитель
d603b76b0c
Коммит
9713c17b08
|
@ -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)
|
||||||
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[] = {
|
||||||
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 @@ unlock:
|
||||||
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 {
|
||||||
{ 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)
|
||||||
|
|
||||||
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;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче