Bluetooth: btmrvl: wait for HOST_SLEEP_ENABLE event in suspend
authorChin-Ran Lo <crlo@marvell.com>
Tue, 1 Jul 2014 21:00:14 +0000 (14:00 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Sep 2014 16:19:22 +0000 (09:19 -0700)
commit 396e04f4bb9afefb0744715dc76d9abe18ee5fb0 upstream.

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.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c

index 7399303..9e81a3d 100644 (file)
@@ -66,6 +66,7 @@ struct btmrvl_adapter {
        u8 hs_state;
        u8 wakeup_tries;
        wait_queue_head_t cmd_wait_q;
+       wait_queue_head_t event_hs_wait_q;
        u8 cmd_complete;
        bool is_suspended;
 };
index 1e0320a..49d2098 100644 (file)
@@ -112,6 +112,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
                        adapter->hs_state = HS_ACTIVATED;
                        if (adapter->psmode)
                                adapter->ps_state = PS_SLEEP;
+                       wake_up_interruptible(&adapter->event_hs_wait_q);
                        BT_DBG("HS ACTIVATED!");
                } else {
                        BT_DBG("HS Enable failed");
@@ -251,11 +252,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
 
 int btmrvl_enable_hs(struct btmrvl_private *priv)
 {
+       struct btmrvl_adapter *adapter = priv->adapter;
        int ret;
 
        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");
+               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;
 }
@@ -341,6 +362,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv)
        priv->adapter->ps_state = PS_AWAKE;
 
        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)
@@ -648,6 +670,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv)
        hdev = priv->btmrvl_dev.hcidev;
 
        wake_up_interruptible(&priv->adapter->cmd_wait_q);
+       wake_up_interruptible(&priv->adapter->event_hs_wait_q);
 
        kthread_stop(priv->main_thread.task);