wl12xx: enter/exit psm on wowlan suspend/resume
authorEliad Peller <eliad@wizery.com>
Fri, 13 May 2011 08:57:13 +0000 (11:57 +0300)
committerLuciano Coelho <coelho@ti.com>
Fri, 13 May 2011 11:55:49 +0000 (14:55 +0300)
When operating as station, enter psm before suspending
the device into wowlan state.

Add a new completion event to signal when psm was entered
successfully.

Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/event.c
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/ps.h
drivers/net/wireless/wl12xx/wl12xx.h

index 1e4bd6a2c3964743acf89889bf65a4b7d8b9540f..c3c554cd6580dd63c31dd369c7b3a1e6294b8d20 100644 (file)
@@ -135,6 +135,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
 
                /* enable beacon early termination */
                ret = wl1271_acx_bet_enable(wl, true);
+               if (ret < 0)
+                       break;
+
+               if (wl->ps_compl) {
+                       complete(wl->ps_compl);
+                       wl->ps_compl = NULL;
+               }
                break;
        default:
                break;
index 8f9e6152f3b7f1d9acbfe1da11c62d4ac90c0d03..610be03a198b99d05321502ee66e66963be184e8 100644 (file)
@@ -1350,6 +1350,79 @@ static struct notifier_block wl1271_dev_notifier = {
        .notifier_call = wl1271_dev_notify,
 };
 
+static int wl1271_configure_suspend(struct wl1271 *wl)
+{
+       int ret;
+
+       if (wl->bss_type != BSS_TYPE_STA_BSS)
+               return 0;
+
+       mutex_lock(&wl->mutex);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       /* enter psm if needed*/
+       if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+               DECLARE_COMPLETION_ONSTACK(compl);
+
+               wl->ps_compl = &compl;
+               ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
+                                  wl->basic_rate, true);
+               if (ret < 0)
+                       goto out_sleep;
+
+               /* we must unlock here so we will be able to get events */
+               wl1271_ps_elp_sleep(wl);
+               mutex_unlock(&wl->mutex);
+
+               ret = wait_for_completion_timeout(
+                       &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT));
+               if (ret <= 0) {
+                       wl1271_warning("couldn't enter ps mode!");
+                       ret = -EBUSY;
+                       goto out;
+               }
+
+               /* take mutex again, and wakeup */
+               mutex_lock(&wl->mutex);
+
+               ret = wl1271_ps_elp_wakeup(wl);
+               if (ret < 0)
+                       goto out_unlock;
+       }
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out_unlock:
+       mutex_unlock(&wl->mutex);
+out:
+       return ret;
+
+}
+
+static void wl1271_configure_resume(struct wl1271 *wl)
+{
+       int ret;
+
+       if (wl->bss_type != BSS_TYPE_STA_BSS)
+               return;
+
+       mutex_lock(&wl->mutex);
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* exit psm if it wasn't configured */
+       if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
+               wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
+                                  wl->basic_rate, true);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl1271_op_suspend(struct ieee80211_hw *hw,
                            struct cfg80211_wowlan *wow)
 {
@@ -1357,6 +1430,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
        wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
        wl->wow_enabled = !!wow;
        if (wl->wow_enabled) {
+               int ret;
+               ret = wl1271_configure_suspend(wl);
+               if (ret < 0) {
+                       wl1271_warning("couldn't prepare device to suspend");
+                       return ret;
+               }
                /* flush any remaining work */
                wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
                flush_delayed_work(&wl->scan_complete_work);
@@ -1408,6 +1487,8 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
                        wl1271_irq(0, wl);
                        wl1271_enable_interrupts(wl);
                }
+
+               wl1271_configure_resume(wl);
        }
 
        return 0;
index c41bd0a711bcd35e19855e6cb80c66a79cf0b4b4..25eb9bc9b628283afffa2ed825967c45256f1db5 100644 (file)
@@ -35,4 +35,6 @@ void wl1271_elp_work(struct work_struct *work);
 void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues);
 void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid);
 
+#define WL1271_PS_COMPLETE_TIMEOUT 500
+
 #endif /* __WL1271_PS_H__ */
index 2218b9c638441688700688807bb26577b6d87d63..fbe8f46d123243efe86d6ce3098f176da2c75628 100644 (file)
@@ -513,6 +513,7 @@ struct wl1271 {
        unsigned int rx_filter;
 
        struct completion *elp_compl;
+       struct completion *ps_compl;
        struct delayed_work elp_work;
        struct delayed_work pspoll_work;