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 | |
| 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>
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 2 | ||||
| -rw-r--r-- | net/bluetooth/hci_core.c | 58 | ||||
| -rw-r--r-- | net/bluetooth/mgmt.c | 119 |
3 files changed, 109 insertions, 70 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8eccdf029500..79724c87ab00 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
| @@ -920,8 +920,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, | |||
| 920 | bdaddr_t *addr, u8 addr_type); | 920 | bdaddr_t *addr, u8 addr_type); |
| 921 | struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, | 921 | struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, |
| 922 | bdaddr_t *addr, u8 addr_type); | 922 | bdaddr_t *addr, u8 addr_type); |
| 923 | int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, | ||
| 924 | u8 auto_connect); | ||
| 925 | void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); | 923 | void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); |
| 926 | void hci_conn_params_clear_all(struct hci_dev *hdev); | 924 | void hci_conn_params_clear_all(struct hci_dev *hdev); |
| 927 | void hci_conn_params_clear_disabled(struct hci_dev *hdev); | 925 | void hci_conn_params_clear_disabled(struct hci_dev *hdev); |
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); |
