Commit 396e04f4 authored by Chin-Ran Lo's avatar Chin-Ran Lo Committed by Marcel Holtmann

Bluetooth: btmrvl: wait for HOST_SLEEP_ENABLE event in suspend

After BT_CMD_HOST_SLEEP_ENABLE command finishes, driver should
wait until getting BT_EVENT_HOST_SLEEP_ENABLE event to complete
suspend procedure.
Without this patch the suspend handler would return success
earlier. By the time when the BT_EVENT_HOST_SLEEP_ENABLE event
comes in the controller driver could have already turned off the
bus clock. This causes kernel crash or system reboot eventually.

Cc: <stable@vger.kernel.org> # 3.13+
Signed-off-by: default avatarChin-Ran Lo <crlo@marvell.com>
Signed-off-by: default avatarJeff CF Chen <jeffc@marvell.com>
Signed-off-by: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 4df82b59
...@@ -68,6 +68,7 @@ struct btmrvl_adapter { ...@@ -68,6 +68,7 @@ struct btmrvl_adapter {
u8 hs_state; u8 hs_state;
u8 wakeup_tries; u8 wakeup_tries;
wait_queue_head_t cmd_wait_q; wait_queue_head_t cmd_wait_q;
wait_queue_head_t event_hs_wait_q;
u8 cmd_complete; u8 cmd_complete;
bool is_suspended; bool is_suspended;
}; };
......
...@@ -114,6 +114,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) ...@@ -114,6 +114,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
adapter->hs_state = HS_ACTIVATED; adapter->hs_state = HS_ACTIVATED;
if (adapter->psmode) if (adapter->psmode)
adapter->ps_state = PS_SLEEP; adapter->ps_state = PS_SLEEP;
wake_up_interruptible(&adapter->event_hs_wait_q);
BT_DBG("HS ACTIVATED!"); BT_DBG("HS ACTIVATED!");
} else { } else {
BT_DBG("HS Enable failed"); BT_DBG("HS Enable failed");
...@@ -270,11 +271,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps); ...@@ -270,11 +271,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
int btmrvl_enable_hs(struct btmrvl_private *priv) int btmrvl_enable_hs(struct btmrvl_private *priv)
{ {
struct btmrvl_adapter *adapter = priv->adapter;
int ret; int ret;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
if (ret) if (ret) {
BT_ERR("Host sleep enable command failed\n"); BT_ERR("Host sleep enable command failed\n");
return ret;
}
ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,
adapter->hs_state,
msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED));
if (ret < 0) {
BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",
ret, adapter->hs_state, adapter->ps_state,
adapter->wakeup_tries);
} else if (!ret) {
BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state,
adapter->ps_state, adapter->wakeup_tries);
ret = -ETIMEDOUT;
} else {
BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state,
adapter->ps_state, adapter->wakeup_tries);
ret = 0;
}
return ret; return ret;
} }
...@@ -375,6 +396,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv) ...@@ -375,6 +396,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv)
} }
init_waitqueue_head(&priv->adapter->cmd_wait_q); init_waitqueue_head(&priv->adapter->cmd_wait_q);
init_waitqueue_head(&priv->adapter->event_hs_wait_q);
} }
static void btmrvl_free_adapter(struct btmrvl_private *priv) static void btmrvl_free_adapter(struct btmrvl_private *priv)
...@@ -685,6 +707,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv) ...@@ -685,6 +707,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv)
hdev = priv->btmrvl_dev.hcidev; hdev = priv->btmrvl_dev.hcidev;
wake_up_interruptible(&priv->adapter->cmd_wait_q); wake_up_interruptible(&priv->adapter->cmd_wait_q);
wake_up_interruptible(&priv->adapter->event_hs_wait_q);
kthread_stop(priv->main_thread.task); kthread_stop(priv->main_thread.task);
......
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