aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2014-12-19 15:26:01 -0500
committerMarcel Holtmann <marcel@holtmann.org>2014-12-19 16:06:37 -0500
commit51ef3ebe7bcc212f4cbbeac48bda26ee90a6edbe (patch)
tree24fde32c04a44dc15045e62c05c17925cd7275f0 /net/bluetooth
parent2cf22218b00f46f93b39a9355b830e9e8e4fd077 (diff)
Bluetooth: Fix Remove Device to wait for HCI before sending cmd_complete
This patch updates the Remove Device mgmt command handler to use a hci_request to wait for HCI command completion before notifying user space of the mgmt command completion. This way we ensure that once the mgmt command returns all HCI commands triggered by it have also completed. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/mgmt.c84
1 files changed, 62 insertions, 22 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 3afe1e175eb8..6b925733c6f8 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -5523,24 +5523,55 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
5523 mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); 5523 mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
5524} 5524}
5525 5525
5526static void remove_device_complete(struct hci_dev *hdev, u8 status)
5527{
5528 struct pending_cmd *cmd;
5529
5530 BT_DBG("status 0x%02x", status);
5531
5532 hci_dev_lock(hdev);
5533
5534 cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
5535 if (!cmd)
5536 goto unlock;
5537
5538 cmd->cmd_complete(cmd, mgmt_status(status));
5539 mgmt_pending_remove(cmd);
5540
5541unlock:
5542 hci_dev_unlock(hdev);
5543}
5544
5526static int remove_device(struct sock *sk, struct hci_dev *hdev, 5545static int remove_device(struct sock *sk, struct hci_dev *hdev,
5527 void *data, u16 len) 5546 void *data, u16 len)
5528{ 5547{
5529 struct mgmt_cp_remove_device *cp = data; 5548 struct mgmt_cp_remove_device *cp = data;
5549 struct pending_cmd *cmd;
5550 struct hci_request req;
5530 int err; 5551 int err;
5531 5552
5532 BT_DBG("%s", hdev->name); 5553 BT_DBG("%s", hdev->name);
5533 5554
5555 hci_req_init(&req, hdev);
5556
5534 hci_dev_lock(hdev); 5557 hci_dev_lock(hdev);
5535 5558
5559 cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
5560 if (!cmd) {
5561 err = -ENOMEM;
5562 goto unlock;
5563 }
5564
5565 cmd->cmd_complete = addr_cmd_complete;
5566
5536 if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { 5567 if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
5537 struct hci_conn_params *params; 5568 struct hci_conn_params *params;
5538 u8 addr_type; 5569 u8 addr_type;
5539 5570
5540 if (!bdaddr_type_is_valid(cp->addr.type)) { 5571 if (!bdaddr_type_is_valid(cp->addr.type)) {
5541 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 5572 err = 0;
5542 MGMT_STATUS_INVALID_PARAMS, 5573 cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
5543 &cp->addr, sizeof(cp->addr)); 5574 mgmt_pending_remove(cmd);
5544 goto unlock; 5575 goto unlock;
5545 } 5576 }
5546 5577
@@ -5549,14 +5580,14 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
5549 &cp->addr.bdaddr, 5580 &cp->addr.bdaddr,
5550 cp->addr.type); 5581 cp->addr.type);
5551 if (err) { 5582 if (err) {
5552 err = cmd_complete(sk, hdev->id, 5583 err = 0;
5553 MGMT_OP_REMOVE_DEVICE, 5584 cmd->cmd_complete(cmd,
5554 MGMT_STATUS_INVALID_PARAMS, 5585 MGMT_STATUS_INVALID_PARAMS);
5555 &cp->addr, sizeof(cp->addr)); 5586 mgmt_pending_remove(cmd);
5556 goto unlock; 5587 goto unlock;
5557 } 5588 }
5558 5589
5559 hci_update_page_scan(hdev); 5590 __hci_update_page_scan(&req);
5560 5591
5561 device_removed(sk, hdev, &cp->addr.bdaddr, 5592 device_removed(sk, hdev, &cp->addr.bdaddr,
5562 cp->addr.type); 5593 cp->addr.type);
@@ -5571,23 +5602,23 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
5571 params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, 5602 params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
5572 addr_type); 5603 addr_type);
5573 if (!params) { 5604 if (!params) {
5574 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 5605 err = 0;
5575 MGMT_STATUS_INVALID_PARAMS, 5606 cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
5576 &cp->addr, sizeof(cp->addr)); 5607 mgmt_pending_remove(cmd);
5577 goto unlock; 5608 goto unlock;
5578 } 5609 }
5579 5610
5580 if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { 5611 if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
5581 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 5612 err = 0;
5582 MGMT_STATUS_INVALID_PARAMS, 5613 cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
5583 &cp->addr, sizeof(cp->addr)); 5614 mgmt_pending_remove(cmd);
5584 goto unlock; 5615 goto unlock;
5585 } 5616 }
5586 5617
5587 list_del(&params->action); 5618 list_del(&params->action);
5588 list_del(&params->list); 5619 list_del(&params->list);
5589 kfree(params); 5620 kfree(params);
5590 hci_update_background_scan(hdev); 5621 __hci_update_background_scan(&req);
5591 5622
5592 device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); 5623 device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
5593 } else { 5624 } else {
@@ -5595,9 +5626,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
5595 struct bdaddr_list *b, *btmp; 5626 struct bdaddr_list *b, *btmp;
5596 5627
5597 if (cp->addr.type) { 5628 if (cp->addr.type) {
5598 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 5629 err = 0;
5599 MGMT_STATUS_INVALID_PARAMS, 5630 cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
5600 &cp->addr, sizeof(cp->addr)); 5631 mgmt_pending_remove(cmd);
5601 goto unlock; 5632 goto unlock;
5602 } 5633 }
5603 5634
@@ -5607,7 +5638,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
5607 kfree(b); 5638 kfree(b);
5608 } 5639 }
5609 5640
5610 hci_update_page_scan(hdev); 5641 __hci_update_page_scan(&req);
5611 5642
5612 list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { 5643 list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
5613 if (p->auto_connect == HCI_AUTO_CONN_DISABLED) 5644 if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -5620,12 +5651,21 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
5620 5651
5621 BT_DBG("All LE connection parameters were removed"); 5652 BT_DBG("All LE connection parameters were removed");
5622 5653
5623 hci_update_background_scan(hdev); 5654 __hci_update_background_scan(&req);
5624 } 5655 }
5625 5656
5626complete: 5657complete:
5627 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 5658 err = hci_req_run(&req, remove_device_complete);
5628 MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); 5659 if (err < 0) {
5660 /* ENODATA means no HCI commands were needed (e.g. if
5661 * the adapter is powered off).
5662 */
5663 if (err == -ENODATA) {
5664 cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
5665 err = 0;
5666 }
5667 mgmt_pending_remove(cmd);
5668 }
5629 5669
5630unlock: 5670unlock:
5631 hci_dev_unlock(hdev); 5671 hci_dev_unlock(hdev);