summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSukesh Srikakula2011-05-13 11:59:39 +0200
committerGreg Kroah-Hartman2011-05-17 21:12:32 +0200
commite6e8f894aac3ab87aa272ed1479fa1f89c30844d (patch)
treef17e034b5bf344e61ea275244b817a1f61a494c2
parentstaging: brcm80211: Fix for WPA GTK install issue in brcmfmac driver (diff)
downloadkernel-qcow2-linux-e6e8f894aac3ab87aa272ed1479fa1f89c30844d.tar.gz
kernel-qcow2-linux-e6e8f894aac3ab87aa272ed1479fa1f89c30844d.tar.xz
kernel-qcow2-linux-e6e8f894aac3ab87aa272ed1479fa1f89c30844d.zip
staging: brcm80211: Fix for suspend issue in brcmfmac driver
Currently, there are 2 callbacks registered with OS for getting notifications when system goes to suspend/resume. Racing between these 2 callbacks results in random suspend failures. With this fix, we avoid registering dhd callback for suspend/resume notification when cfg80211 is used. Relevent functionality in dhd suspend/resume callback function is moved to cfg80211 suspend/resume functions. Cc: devel@linuxdriverproject.org Cc: linux-wireless@vger.kernel.org Cc: Grant Grundler <grundler@chromium.org> Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Brett Rudley <brudley@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c5
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd.h22
-rw-r--r--drivers/staging/brcm80211/brcmfmac/dhd_linux.c20
-rw-r--r--drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c77
-rw-r--r--drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h1
5 files changed, 97 insertions, 28 deletions
diff --git a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 43aebfd135a9..c0ffbd35e0ca 100644
--- a/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/staging/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -26,14 +26,11 @@
#include <linux/mmc/core.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/suspend.h>
#include <dngl_stats.h>
#include <dhd.h>
-#if defined(CONFIG_PM_SLEEP)
-#include <linux/suspend.h>
-extern volatile bool dhd_mmc_suspend;
-#endif
#include "bcmsdh_sdmmc.h"
extern int sdio_function_init(void);
diff --git a/drivers/staging/brcm80211/brcmfmac/dhd.h b/drivers/staging/brcm80211/brcmfmac/dhd.h
index 99c38dd8cb74..a726b493ea8d 100644
--- a/drivers/staging/brcm80211/brcmfmac/dhd.h
+++ b/drivers/staging/brcm80211/brcmfmac/dhd.h
@@ -31,6 +31,7 @@
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
+#include <linux/suspend.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
/* The kernel threading is sdio-specific */
@@ -122,19 +123,22 @@ typedef struct dhd_pub {
} dhd_pub_t;
#if defined(CONFIG_PM_SLEEP)
-
+extern atomic_t dhd_mmc_suspend;
#define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
-#define _DHD_PM_RESUME_WAIT(a, b) do {\
- int retry = 0; \
- while (dhd_mmc_suspend && retry++ != b) { \
- wait_event_timeout(a, false, HZ/100); \
- } \
- } while (0)
+#define _DHD_PM_RESUME_WAIT(a, b) do { \
+ int retry = 0; \
+ while (atomic_read(&dhd_mmc_suspend) && retry++ != b) { \
+ wait_event_timeout(a, false, HZ/100); \
+ } \
+ } while (0)
#define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 30)
#define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0)
#define DHD_PM_RESUME_RETURN_ERROR(a) \
- do { if (dhd_mmc_suspend) return a; } while (0)
-#define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0)
+ do { if (atomic_read(&dhd_mmc_suspend)) return a; } while (0)
+#define DHD_PM_RESUME_RETURN do { \
+ if (atomic_read(&dhd_mmc_suspend)) \
+ return; \
+ } while (0)
#define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
#define SPINWAIT_SLEEP(a, exp, us) do { \
diff --git a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
index ba73ce021a37..31a5ca05fca7 100644
--- a/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/staging/brcm80211/brcmfmac/dhd_linux.c
@@ -166,7 +166,7 @@ void wifi_del_dev(void)
#if defined(CONFIG_PM_SLEEP)
#include <linux/suspend.h>
-volatile bool dhd_mmc_suspend = false;
+atomic_t dhd_mmc_suspend;
DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
#endif /* defined(CONFIG_PM_SLEEP) */
@@ -407,11 +407,11 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb,
switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
- dhd_mmc_suspend = true;
+ atomic_set(&dhd_mmc_suspend, true);
return NOTIFY_OK;
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
- dhd_mmc_suspend = false;
+ atomic_set(&dhd_mmc_suspend, false);
return NOTIFY_OK;
}
return 0;
@@ -2011,7 +2011,9 @@ dhd_pub_t *dhd_attach(struct dhd_bus *bus, uint bus_hdrlen)
g_bus = bus;
#endif
#if defined(CONFIG_PM_SLEEP)
- register_pm_notifier(&dhd_sleep_pm_notifier);
+ atomic_set(&dhd_mmc_suspend, false);
+ if (!IS_CFG80211_FAVORITE())
+ register_pm_notifier(&dhd_sleep_pm_notifier);
#endif /* defined(CONFIG_PM_SLEEP) */
/* && defined(DHD_GPL) */
/* Init lock suspend to prevent kernel going to suspend */
@@ -2305,7 +2307,8 @@ void dhd_detach(dhd_pub_t *dhdp)
wl_cfg80211_detach();
#if defined(CONFIG_PM_SLEEP)
- unregister_pm_notifier(&dhd_sleep_pm_notifier);
+ if (!IS_CFG80211_FAVORITE())
+ unregister_pm_notifier(&dhd_sleep_pm_notifier);
#endif /* defined(CONFIG_PM_SLEEP) */
/* && defined(DHD_GPL) */
free_netdev(ifp->net);
@@ -2816,6 +2819,13 @@ int dhd_wait_pend8021x(struct net_device *dev)
return pend;
}
+void wl_os_wd_timer(struct net_device *ndev, uint wdtick)
+{
+ dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(ndev);
+
+ dhd_os_wd_timer(&dhd->pub, wdtick);
+}
+
#ifdef DHD_DEBUG
int write_to_file(dhd_pub_t *dhd, u8 *buf, int size)
{
diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
index c60fc7cb7436..2d67048d0759 100644
--- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
@@ -1969,34 +1969,91 @@ wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev,
static s32 wl_cfg80211_resume(struct wiphy *wiphy)
{
- s32 err = 0;
+ struct wl_priv *wl = wiphy_to_wl(wiphy);
+ struct net_device *ndev = wl_to_ndev(wl);
- CHECK_SYS_UP();
- wl_invoke_iscan(wiphy_to_wl(wiphy));
+ /*
+ * Check for WL_STATUS_READY before any function call which
+ * could result is bus access. Don't block the resume for
+ * any driver error conditions
+ */
- return err;
+#if defined(CONFIG_PM_SLEEP)
+ atomic_set(&dhd_mmc_suspend, false);
+#endif /* defined(CONFIG_PM_SLEEP) */
+
+ if (test_bit(WL_STATUS_READY, &wl->status)) {
+ /* Turn on Watchdog timer */
+ wl_os_wd_timer(ndev, dhd_watchdog_ms);
+ wl_invoke_iscan(wiphy_to_wl(wiphy));
+ }
+
+ return 0;
}
static s32 wl_cfg80211_suspend(struct wiphy *wiphy)
{
struct wl_priv *wl = wiphy_to_wl(wiphy);
struct net_device *ndev = wl_to_ndev(wl);
- s32 err = 0;
+
+
+ /*
+ * Check for WL_STATUS_READY before any function call which
+ * could result is bus access. Don't block the suspend for
+ * any driver error conditions
+ */
+
+ /*
+ * While going to suspend if associated with AP disassociate
+ * from AP to save power while system is in suspended state
+ */
+ if (test_bit(WL_STATUS_CONNECTED, &wl->status) &&
+ test_bit(WL_STATUS_READY, &wl->status)) {
+ WL_INFO("Disassociating from AP"
+ " while entering suspend state\n");
+ wl_link_down(wl);
+
+ /*
+ * Make sure WPA_Supplicant receives all the event
+ * generated due to DISASSOC call to the fw to keep
+ * the state fw and WPA_Supplicant state consistent
+ */
+ rtnl_unlock();
+ wl_delay(500);
+ rtnl_lock();
+ }
set_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
- wl_term_iscan(wl);
+ if (test_bit(WL_STATUS_READY, &wl->status))
+ wl_term_iscan(wl);
+
if (wl->scan_request) {
- cfg80211_scan_done(wl->scan_request, true); /* true means
- abort */
- wl_set_mpc(ndev, 1);
+ /* Indidate scan abort to cfg80211 layer */
+ WL_INFO("Terminating scan in progress\n");
+ cfg80211_scan_done(wl->scan_request, true);
wl->scan_request = NULL;
}
clear_bit(WL_STATUS_SCANNING, &wl->status);
clear_bit(WL_STATUS_SCAN_ABORTING, &wl->status);
+ clear_bit(WL_STATUS_CONNECTING, &wl->status);
+ clear_bit(WL_STATUS_CONNECTED, &wl->status);
+ /* Inform SDIO stack not to switch off power to the chip */
sdioh_sdio_set_host_pm_flags(MMC_PM_KEEP_POWER);
- return err;
+ /* Turn off watchdog timer */
+ if (test_bit(WL_STATUS_READY, &wl->status)) {
+ WL_INFO("Terminate watchdog timer and enable MPC\n");
+ wl_set_mpc(ndev, 1);
+ wl_os_wd_timer(ndev, 0);
+ }
+
+#if defined(CONFIG_PM_SLEEP)
+ atomic_set(&dhd_mmc_suspend, true);
+#endif /* defined(CONFIG_PM_SLEEP) */
+
+
+ return 0;
}
static __used s32
diff --git a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
index 5112160e0ae3..3c8b90294d01 100644
--- a/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/staging/brcm80211/brcmfmac/wl_cfg80211.h
@@ -375,5 +375,6 @@ extern s8 *wl_cfg80211_get_fwname(void); /* get firmware name for
the dongle */
extern s8 *wl_cfg80211_get_nvramname(void); /* get nvram name for
the dongle */
+extern void wl_os_wd_timer(struct net_device *ndev, uint wdtick);
#endif /* _wl_cfg80211_h_ */