diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2014-12-19 15:26:02 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-12-19 16:06:37 -0500 |
commit | 5a154e6f71dfd41c7b5cf96a13c83fca91e7df7f (patch) | |
tree | 26ae6a6727b3750b5960134b21b7f6a9e26fda6a /net | |
parent | 51ef3ebe7bcc212f4cbbeac48bda26ee90a6edbe (diff) |
Bluetooth: Fix Add Device to wait for HCI before sending cmd_complete
This patch updates the Add Device mgmt command handler to use a
hci_request to wait for HCI command completion before notifying user
space of the mgmt command completion. To do this we need to add an extra
hci_request parameter to the hci_conn_params_set function. Since this
function has no other users besides mgmt.c it's moved there as a static
function.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hci_core.c | 58 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 119 |
2 files changed, 109 insertions, 68 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index def6fba01b45..ee2096c7ec2c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
@@ -3660,23 +3660,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, | |||
3660 | return NULL; | 3660 | return NULL; |
3661 | } | 3661 | } |
3662 | 3662 | ||
3663 | static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) | ||
3664 | { | ||
3665 | struct hci_conn *conn; | ||
3666 | |||
3667 | conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); | ||
3668 | if (!conn) | ||
3669 | return false; | ||
3670 | |||
3671 | if (conn->dst_type != type) | ||
3672 | return false; | ||
3673 | |||
3674 | if (conn->state != BT_CONNECTED) | ||
3675 | return false; | ||
3676 | |||
3677 | return true; | ||
3678 | } | ||
3679 | |||
3680 | /* This function requires the caller holds hdev->lock */ | 3663 | /* This function requires the caller holds hdev->lock */ |
3681 | struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, | 3664 | struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, |
3682 | bdaddr_t *addr, u8 addr_type) | 3665 | bdaddr_t *addr, u8 addr_type) |
@@ -3732,47 +3715,6 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, | |||
3732 | return params; | 3715 | return params; |
3733 | } | 3716 | } |
3734 | 3717 | ||
3735 | /* This function requires the caller holds hdev->lock */ | ||
3736 | int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, | ||
3737 | u8 auto_connect) | ||
3738 | { | ||
3739 | struct hci_conn_params *params; | ||
3740 | |||
3741 | params = hci_conn_params_add(hdev, addr, addr_type); | ||
3742 | if (!params) | ||
3743 | return -EIO; | ||
3744 | |||
3745 | if (params->auto_connect == auto_connect) | ||
3746 | return 0; | ||
3747 | |||
3748 | list_del_init(¶ms->action); | ||
3749 | |||
3750 | switch (auto_connect) { | ||
3751 | case HCI_AUTO_CONN_DISABLED: | ||
3752 | case HCI_AUTO_CONN_LINK_LOSS: | ||
3753 | hci_update_background_scan(hdev); | ||
3754 | break; | ||
3755 | case HCI_AUTO_CONN_REPORT: | ||
3756 | list_add(¶ms->action, &hdev->pend_le_reports); | ||
3757 | hci_update_background_scan(hdev); | ||
3758 | break; | ||
3759 | case HCI_AUTO_CONN_DIRECT: | ||
3760 | case HCI_AUTO_CONN_ALWAYS: | ||
3761 | if (!is_connected(hdev, addr, addr_type)) { | ||
3762 | list_add(¶ms->action, &hdev->pend_le_conns); | ||
3763 | hci_update_background_scan(hdev); | ||
3764 | } | ||
3765 | break; | ||
3766 | } | ||
3767 | |||
3768 | params->auto_connect = auto_connect; | ||
3769 | |||
3770 | BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, | ||
3771 | auto_connect); | ||
3772 | |||
3773 | return 0; | ||
3774 | } | ||
3775 | |||
3776 | static void hci_conn_params_free(struct hci_conn_params *params) | 3718 | static void hci_conn_params_free(struct hci_conn_params *params) |
3777 | { | 3719 | { |
3778 | if (params->conn) { | 3720 | if (params->conn) { |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6b925733c6f8..ec7c0ec3d8d3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -5425,6 +5425,65 @@ unlock: | |||
5425 | return err; | 5425 | return err; |
5426 | } | 5426 | } |
5427 | 5427 | ||
5428 | static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) | ||
5429 | { | ||
5430 | struct hci_conn *conn; | ||
5431 | |||
5432 | conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); | ||
5433 | if (!conn) | ||
5434 | return false; | ||
5435 | |||
5436 | if (conn->dst_type != type) | ||
5437 | return false; | ||
5438 | |||
5439 | if (conn->state != BT_CONNECTED) | ||
5440 | return false; | ||
5441 | |||
5442 | return true; | ||
5443 | } | ||
5444 | |||
5445 | /* This function requires the caller holds hdev->lock */ | ||
5446 | static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr, | ||
5447 | u8 addr_type, u8 auto_connect) | ||
5448 | { | ||
5449 | struct hci_dev *hdev = req->hdev; | ||
5450 | struct hci_conn_params *params; | ||
5451 | |||
5452 | params = hci_conn_params_add(hdev, addr, addr_type); | ||
5453 | if (!params) | ||
5454 | return -EIO; | ||
5455 | |||
5456 | if (params->auto_connect == auto_connect) | ||
5457 | return 0; | ||
5458 | |||
5459 | list_del_init(¶ms->action); | ||
5460 | |||
5461 | switch (auto_connect) { | ||
5462 | case HCI_AUTO_CONN_DISABLED: | ||
5463 | case HCI_AUTO_CONN_LINK_LOSS: | ||
5464 | __hci_update_background_scan(req); | ||
5465 | break; | ||
5466 | case HCI_AUTO_CONN_REPORT: | ||
5467 | list_add(¶ms->action, &hdev->pend_le_reports); | ||
5468 | __hci_update_background_scan(req); | ||
5469 | break; | ||
5470 | case HCI_AUTO_CONN_DIRECT: | ||
5471 | case HCI_AUTO_CONN_ALWAYS: | ||
5472 | if (!is_connected(hdev, addr, addr_type)) { | ||
5473 | list_add(¶ms->action, &hdev->pend_le_conns); | ||
5474 | __hci_update_background_scan(req); | ||
5475 | } | ||
5476 | break; | ||
5477 | } | ||
5478 | |||
5479 | params->auto_connect = auto_connect; | ||
5480 | |||
5481 | BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, | ||
5482 | auto_connect); | ||
5483 | |||
5484 | return 0; | ||
5485 | } | ||
5486 | |||
5428 | static void device_added(struct sock *sk, struct hci_dev *hdev, | 5487 | static void device_added(struct sock *sk, struct hci_dev *hdev, |
5429 | bdaddr_t *bdaddr, u8 type, u8 action) | 5488 | bdaddr_t *bdaddr, u8 type, u8 action) |
5430 | { | 5489 | { |
@@ -5437,10 +5496,31 @@ static void device_added(struct sock *sk, struct hci_dev *hdev, | |||
5437 | mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); | 5496 | mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); |
5438 | } | 5497 | } |
5439 | 5498 | ||
5499 | static void add_device_complete(struct hci_dev *hdev, u8 status) | ||
5500 | { | ||
5501 | struct pending_cmd *cmd; | ||
5502 | |||
5503 | BT_DBG("status 0x%02x", status); | ||
5504 | |||
5505 | hci_dev_lock(hdev); | ||
5506 | |||
5507 | cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev); | ||
5508 | if (!cmd) | ||
5509 | goto unlock; | ||
5510 | |||
5511 | cmd->cmd_complete(cmd, mgmt_status(status)); | ||
5512 | mgmt_pending_remove(cmd); | ||
5513 | |||
5514 | unlock: | ||
5515 | hci_dev_unlock(hdev); | ||
5516 | } | ||
5517 | |||
5440 | static int add_device(struct sock *sk, struct hci_dev *hdev, | 5518 | static int add_device(struct sock *sk, struct hci_dev *hdev, |
5441 | void *data, u16 len) | 5519 | void *data, u16 len) |
5442 | { | 5520 | { |
5443 | struct mgmt_cp_add_device *cp = data; | 5521 | struct mgmt_cp_add_device *cp = data; |
5522 | struct pending_cmd *cmd; | ||
5523 | struct hci_request req; | ||
5444 | u8 auto_conn, addr_type; | 5524 | u8 auto_conn, addr_type; |
5445 | int err; | 5525 | int err; |
5446 | 5526 | ||
@@ -5457,14 +5537,24 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, | |||
5457 | MGMT_STATUS_INVALID_PARAMS, | 5537 | MGMT_STATUS_INVALID_PARAMS, |
5458 | &cp->addr, sizeof(cp->addr)); | 5538 | &cp->addr, sizeof(cp->addr)); |
5459 | 5539 | ||
5540 | hci_req_init(&req, hdev); | ||
5541 | |||
5460 | hci_dev_lock(hdev); | 5542 | hci_dev_lock(hdev); |
5461 | 5543 | ||
5544 | cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len); | ||
5545 | if (!cmd) { | ||
5546 | err = -ENOMEM; | ||
5547 | goto unlock; | ||
5548 | } | ||
5549 | |||
5550 | cmd->cmd_complete = addr_cmd_complete; | ||
5551 | |||
5462 | if (cp->addr.type == BDADDR_BREDR) { | 5552 | if (cp->addr.type == BDADDR_BREDR) { |
5463 | /* Only incoming connections action is supported for now */ | 5553 | /* Only incoming connections action is supported for now */ |
5464 | if (cp->action != 0x01) { | 5554 | if (cp->action != 0x01) { |
5465 | err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, | 5555 | err = 0; |
5466 | MGMT_STATUS_INVALID_PARAMS, | 5556 | cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS); |
5467 | &cp->addr, sizeof(cp->addr)); | 5557 | mgmt_pending_remove(cmd); |
5468 | goto unlock; | 5558 | goto unlock; |
5469 | } | 5559 | } |
5470 | 5560 | ||
@@ -5473,7 +5563,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, | |||
5473 | if (err) | 5563 | if (err) |
5474 | goto unlock; | 5564 | goto unlock; |
5475 | 5565 | ||
5476 | hci_update_page_scan(hdev); | 5566 | __hci_update_page_scan(&req); |
5477 | 5567 | ||
5478 | goto added; | 5568 | goto added; |
5479 | } | 5569 | } |
@@ -5493,19 +5583,28 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, | |||
5493 | /* If the connection parameters don't exist for this device, | 5583 | /* If the connection parameters don't exist for this device, |
5494 | * they will be created and configured with defaults. | 5584 | * they will be created and configured with defaults. |
5495 | */ | 5585 | */ |
5496 | if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, | 5586 | if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type, |
5497 | auto_conn) < 0) { | 5587 | auto_conn) < 0) { |
5498 | err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, | 5588 | err = 0; |
5499 | MGMT_STATUS_FAILED, | 5589 | cmd->cmd_complete(cmd, MGMT_STATUS_FAILED); |
5500 | &cp->addr, sizeof(cp->addr)); | 5590 | mgmt_pending_remove(cmd); |
5501 | goto unlock; | 5591 | goto unlock; |
5502 | } | 5592 | } |
5503 | 5593 | ||
5504 | added: | 5594 | added: |
5505 | device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); | 5595 | device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); |
5506 | 5596 | ||
5507 | err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, | 5597 | err = hci_req_run(&req, add_device_complete); |
5508 | MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); | 5598 | if (err < 0) { |
5599 | /* ENODATA means no HCI commands were needed (e.g. if | ||
5600 | * the adapter is powered off). | ||
5601 | */ | ||
5602 | if (err == -ENODATA) { | ||
5603 | cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS); | ||
5604 | err = 0; | ||
5605 | } | ||
5606 | mgmt_pending_remove(cmd); | ||
5607 | } | ||
5509 | 5608 | ||
5510 | unlock: | 5609 | unlock: |
5511 | hci_dev_unlock(hdev); | 5610 | hci_dev_unlock(hdev); |