Commit 13928971 authored by Johan Hedberg's avatar Johan Hedberg Committed by Gustavo Padovan

Bluetooth: Fix waiting for EIR update when setting local name

We shouldn't respond to the mgmt_set_local_name command until all
related HCI commands have completed. This patch fixes the issue by
running the local name HCI command and the EIR update in the same
asynchronous request, and returning the mgmt command complete through
the complete callback of the request.

The downside of this is that we must set hdev->dev_name before the local
name HCI command has completed since otherwise the generated EIR
command doesn't contain the new name. This means that we can no-longer
reliably detect when the name has really changed and when not. Luckily
this only affects scenarios where the mgmt interface is *not* used (e.g.
hciconfig) so redundant mgmt_ev_local_name_changed events in these cases
are an acceptable drawback.
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
Acked-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent 35b973c9
...@@ -2358,15 +2358,44 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, ...@@ -2358,15 +2358,44 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
HCI_OP_USER_PASSKEY_NEG_REPLY, 0); HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
} }
static void update_name(struct hci_request *req, const char *name) static void update_name(struct hci_request *req)
{ {
struct hci_dev *hdev = req->hdev;
struct hci_cp_write_local_name cp; struct hci_cp_write_local_name cp;
memcpy(cp.name, name, sizeof(cp.name)); memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
} }
static void set_name_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_cp_set_local_name *cp;
struct pending_cmd *cmd;
BT_DBG("status 0x%02x", status);
hci_dev_lock(hdev);
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd)
goto unlock;
cp = cmd->param;
if (status)
cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
mgmt_status(status));
else
cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
cp, sizeof(*cp));
mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
}
static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len) u16 len)
{ {
...@@ -2401,9 +2430,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -2401,9 +2430,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed; goto failed;
} }
memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name));
hci_req_init(&req, hdev); hci_req_init(&req, hdev);
update_name(&req, cp->name); update_name(&req);
err = hci_req_run(&req, NULL); update_eir(&req);
err = hci_req_run(&req, set_name_complete);
if (err < 0) if (err < 0)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
...@@ -3208,7 +3240,7 @@ static int powered_update_hci(struct hci_dev *hdev) ...@@ -3208,7 +3240,7 @@ static int powered_update_hci(struct hci_dev *hdev)
if (lmp_bredr_capable(hdev)) { if (lmp_bredr_capable(hdev)) {
set_bredr_scan(&req); set_bredr_scan(&req);
update_class(&req); update_class(&req);
update_name(&req, hdev->dev_name); update_name(&req);
update_eir(&req); update_eir(&req);
} }
...@@ -3776,59 +3808,29 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, ...@@ -3776,59 +3808,29 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
{ {
struct pending_cmd *cmd;
struct mgmt_cp_set_local_name ev; struct mgmt_cp_set_local_name ev;
bool changed = false; struct pending_cmd *cmd;
int err = 0;
if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) { if (status)
memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); return 0;
changed = true;
}
memset(&ev, 0, sizeof(ev)); memset(&ev, 0, sizeof(ev));
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd) if (!cmd) {
goto send_event; memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
/* Always assume that either the short or the complete name has
* changed if there was a pending mgmt command */
changed = true;
if (status) {
err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
mgmt_status(status));
goto failed;
}
err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
sizeof(ev));
if (err < 0)
goto failed;
send_event:
if (changed)
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
sizeof(ev), cmd ? cmd->sk : NULL);
/* EIR is taken care of separately when powering on the /* If this is a HCI command related to powering on the
* adapter so only update them here if this is a name change * HCI dev don't send any mgmt signals.
* unrelated to power on. */
*/ if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
if (!test_bit(HCI_INIT, &hdev->flags)) { return 0;
struct hci_request req;
hci_req_init(&req, hdev);
update_eir(&req);
hci_req_run(&req, NULL);
} }
failed: return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
if (cmd) cmd ? cmd->sk : NULL);
mgmt_pending_remove(cmd);
return err;
} }
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
......
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