diff options
author | Chin-Ran Lo <crlo@marvell.com> | 2014-07-01 17:00:14 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-07-03 11:42:54 -0400 |
commit | 396e04f4bb9afefb0744715dc76d9abe18ee5fb0 (patch) | |
tree | 0492fff5438d6a4b094489e8faa4536cb5d46689 | |
parent | 4df82b5911c0e380d8b308958f158c3e7b365467 (diff) |
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: Chin-Ran Lo <crlo@marvell.com>
Signed-off-by: Jeff CF Chen <jeffc@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r-- | drivers/bluetooth/btmrvl_drv.h | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 25 |
2 files changed, 25 insertions, 1 deletions
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 4c313e73df83..a8a9d3380e59 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h | |||
@@ -68,6 +68,7 @@ struct btmrvl_adapter { | |||
68 | u8 hs_state; | 68 | u8 hs_state; |
69 | u8 wakeup_tries; | 69 | u8 wakeup_tries; |
70 | wait_queue_head_t cmd_wait_q; | 70 | wait_queue_head_t cmd_wait_q; |
71 | wait_queue_head_t event_hs_wait_q; | ||
71 | u8 cmd_complete; | 72 | u8 cmd_complete; |
72 | bool is_suspended; | 73 | bool is_suspended; |
73 | }; | 74 | }; |
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index d35f2e189a6d..cc65fd2fe856 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c | |||
@@ -114,6 +114,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) | |||
114 | adapter->hs_state = HS_ACTIVATED; | 114 | adapter->hs_state = HS_ACTIVATED; |
115 | if (adapter->psmode) | 115 | if (adapter->psmode) |
116 | adapter->ps_state = PS_SLEEP; | 116 | adapter->ps_state = PS_SLEEP; |
117 | wake_up_interruptible(&adapter->event_hs_wait_q); | ||
117 | BT_DBG("HS ACTIVATED!"); | 118 | BT_DBG("HS ACTIVATED!"); |
118 | } else { | 119 | } else { |
119 | BT_DBG("HS Enable failed"); | 120 | BT_DBG("HS Enable failed"); |
@@ -270,11 +271,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps); | |||
270 | 271 | ||
271 | int btmrvl_enable_hs(struct btmrvl_private *priv) | 272 | int btmrvl_enable_hs(struct btmrvl_private *priv) |
272 | { | 273 | { |
274 | struct btmrvl_adapter *adapter = priv->adapter; | ||
273 | int ret; | 275 | int ret; |
274 | 276 | ||
275 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); | 277 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); |
276 | if (ret) | 278 | if (ret) { |
277 | BT_ERR("Host sleep enable command failed\n"); | 279 | BT_ERR("Host sleep enable command failed\n"); |
280 | return ret; | ||
281 | } | ||
282 | |||
283 | ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, | ||
284 | adapter->hs_state, | ||
285 | msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED)); | ||
286 | if (ret < 0) { | ||
287 | BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", | ||
288 | ret, adapter->hs_state, adapter->ps_state, | ||
289 | adapter->wakeup_tries); | ||
290 | } else if (!ret) { | ||
291 | BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state, | ||
292 | adapter->ps_state, adapter->wakeup_tries); | ||
293 | ret = -ETIMEDOUT; | ||
294 | } else { | ||
295 | BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state, | ||
296 | adapter->ps_state, adapter->wakeup_tries); | ||
297 | ret = 0; | ||
298 | } | ||
278 | 299 | ||
279 | return ret; | 300 | return ret; |
280 | } | 301 | } |
@@ -375,6 +396,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv) | |||
375 | } | 396 | } |
376 | 397 | ||
377 | init_waitqueue_head(&priv->adapter->cmd_wait_q); | 398 | init_waitqueue_head(&priv->adapter->cmd_wait_q); |
399 | init_waitqueue_head(&priv->adapter->event_hs_wait_q); | ||
378 | } | 400 | } |
379 | 401 | ||
380 | static void btmrvl_free_adapter(struct btmrvl_private *priv) | 402 | static void btmrvl_free_adapter(struct btmrvl_private *priv) |
@@ -685,6 +707,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv) | |||
685 | hdev = priv->btmrvl_dev.hcidev; | 707 | hdev = priv->btmrvl_dev.hcidev; |
686 | 708 | ||
687 | wake_up_interruptible(&priv->adapter->cmd_wait_q); | 709 | wake_up_interruptible(&priv->adapter->cmd_wait_q); |
710 | wake_up_interruptible(&priv->adapter->event_hs_wait_q); | ||
688 | 711 | ||
689 | kthread_stop(priv->main_thread.task); | 712 | kthread_stop(priv->main_thread.task); |
690 | 713 | ||