Bluetooth: hci_sync: Only allow hci_cmd_sync_queue if running

This makes sure hci_cmd_sync_queue only queue new work if HCI_RUNNING
has been set otherwise there is a risk of commands being sent while
turning off.

Because hci_cmd_sync_queue can no longer queue work while HCI_RUNNING is
not set it cannot be used to power on adapters so instead
hci_cmd_sync_submit is introduced which bypass the HCI_RUNNING check, so
it behaves like the old implementation.

Link: https://lore.kernel.org/all/CAB4PzUpDMvdc8j2MdeSAy1KkAE-D3woprCwAdYWeOc-3v3c9Sw@mail.gmail.com/Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 20981ce2
...@@ -41,6 +41,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev); ...@@ -41,6 +41,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev);
void hci_cmd_sync_cancel(struct hci_dev *hdev, int err); void hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err); void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err);
int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy); void *data, hci_cmd_sync_work_destroy_t destroy);
......
...@@ -684,7 +684,11 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err) ...@@ -684,7 +684,11 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
} }
EXPORT_SYMBOL(hci_cmd_sync_cancel); EXPORT_SYMBOL(hci_cmd_sync_cancel);
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, /* Submit HCI command to be run in as cmd_sync_work:
*
* - hdev must _not_ be unregistered
*/
int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy) void *data, hci_cmd_sync_work_destroy_t destroy)
{ {
struct hci_cmd_sync_work_entry *entry; struct hci_cmd_sync_work_entry *entry;
...@@ -708,6 +712,23 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func, ...@@ -708,6 +712,23 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
return 0; return 0;
} }
EXPORT_SYMBOL(hci_cmd_sync_submit);
/* Queue HCI command:
*
* - hdev must be running
*/
int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
/* Only queue command if hdev is running which means it had been opened
* and is either on init phase or is already up.
*/
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -ENETDOWN;
return hci_cmd_sync_submit(hdev, func, data, destroy);
}
EXPORT_SYMBOL(hci_cmd_sync_queue); EXPORT_SYMBOL(hci_cmd_sync_queue);
int hci_update_eir_sync(struct hci_dev *hdev) int hci_update_eir_sync(struct hci_dev *hdev)
......
...@@ -1400,11 +1400,15 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -1400,11 +1400,15 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
} }
/* Cancel potentially blocking sync operation before power off */ /* Cancel potentially blocking sync operation before power off */
if (cp->val == 0x00) if (cp->val == 0x00) {
__hci_cmd_sync_cancel(hdev, -EHOSTDOWN); __hci_cmd_sync_cancel(hdev, -EHOSTDOWN);
err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd, err = hci_cmd_sync_queue(hdev, set_powered_sync, cmd,
mgmt_set_powered_complete); mgmt_set_powered_complete);
} else {
/* Use hci_cmd_sync_submit since hdev might not be running */
err = hci_cmd_sync_submit(hdev, set_powered_sync, cmd,
mgmt_set_powered_complete);
}
if (err < 0) if (err < 0)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
......
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