wl1251: experimental PS hacks
authorGrazvydas Ignotas <notasas@gmail.com>
Wed, 22 May 2013 00:40:14 +0000 (03:40 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Thu, 23 May 2013 22:13:09 +0000 (01:13 +0300)
drivers/net/wireless/wl1251/event.c
drivers/net/wireless/wl1251/main.c
drivers/net/wireless/wl1251/ps.c
drivers/net/wireless/wl1251/rx.c
drivers/net/wireless/wl1251/tx.c
drivers/net/wireless/wl1251/wl1251.h

index 94932b1..bd1e90f 100644 (file)
@@ -67,13 +67,8 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
 
        if (vector & BSS_LOSE_EVENT_ID) {
                wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
-
-               if (wl->psm_requested &&
-                   wl->station_mode != STATION_ACTIVE_MODE) {
-                       ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
-                       if (ret < 0)
-                               return ret;
-               }
+               wl->bss_lost = 1;
+               ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
        }
 
        if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
@@ -85,11 +80,8 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
        }
 
        if (vector & REGAINED_BSS_EVENT_ID) {
-               if (wl->psm_requested) {
-                       ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
-                       if (ret < 0)
-                               return ret;
-               }
+               wl->bss_lost = 0;
+               ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
        }
 
        if (wl->vif && wl->rssi_thold) {
@@ -121,6 +113,9 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
                        wl->station_mode = STATION_ACTIVE_MODE;
                else if (mbox->ps_status == EXIT_POWER_SAVE_FAIL)
                        wl->station_mode = STATION_POWER_SAVE_MODE;
+
+               //wl1251_error("ps_status %d", mbox->ps_status);
+               wl->ps_transitioning = false;
        }
 
        return 0;
index 84a5bc9..4706d50 100644 (file)
@@ -457,6 +457,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
 
        cancel_work_sync(&wl->irq_work);
        cancel_work_sync(&wl->tx_work);
+       cancel_delayed_work_sync(&wl->ps_work);
        cancel_delayed_work_sync(&wl->elp_work);
 
        mutex_lock(&wl->mutex);
@@ -561,6 +562,92 @@ static int wl1251_build_qos_null_data(struct wl1251 *wl)
                                       sizeof(template));
 }
 
+static void wl1251_ps_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1251 *wl;
+       unsigned long diff, wait;
+       bool need_ps;
+       bool have_ps;
+       int ret;
+       int i;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wl = container_of(dwork, struct wl1251, ps_work);
+
+       mutex_lock(&wl->mutex);
+
+       /* don't change PS modes while still transitioning, to avoid possbile
+        * fw bugs (it normally takes ~130ms to enable and ~10ms to disable) */
+       if (wl->ps_transitioning) {
+               diff = jiffies - wl->ps_change_jiffies;
+               if (diff > msecs_to_jiffies(500)) {
+                       wl1251_error("PS change taking too long: %lu", diff);
+                       wl->ps_transitioning = false;
+               } else {
+                       //wl1251_error("PS still transitioning");
+                       ieee80211_queue_delayed_work(wl->hw, &wl->ps_work,
+                               msecs_to_jiffies(50));
+                       goto out;
+               }
+       }
+
+       need_ps = wl->psm_requested && !wl->bss_lost;
+       have_ps = wl->station_mode == STATION_POWER_SAVE_MODE;
+
+       if (need_ps == have_ps) {
+               //wl1251_info("ps: already in mode %d", have_ps);
+               goto out;
+       }
+
+       /* don't enter PS if there was recent activity */
+       if (need_ps) {
+               wait = 0;
+
+               diff = jiffies - wl->last_io_jiffies;
+               if (diff < msecs_to_jiffies(150)) {
+                       //wl1251_info("ps: postponed psm, j %ld", diff);
+                       wait = msecs_to_jiffies(150) - diff + 1;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(wl->tx_frames); i++) {
+                       if (wl->tx_frames[i] != NULL) {
+                               //wl1251_error("  frm %d busy", i);
+                               if (wait < msecs_to_jiffies(50))
+                                       wait = msecs_to_jiffies(50);
+                               break;
+                       }
+               }
+
+               if (wait > 0) {
+                       ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, wait);
+                       goto out;
+               }
+       }
+
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       if (need_ps) {
+               wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
+                                           wl->dtim_period);
+       }
+       ret = wl1251_ps_set_mode(wl,
+               need_ps ? STATION_POWER_SAVE_MODE : STATION_ACTIVE_MODE);
+       if (ret < 0)
+               goto out_sleep;
+
+       //wl1251_info("psm %d, j %ld, d %ld", need_ps,
+       //      jiffies - wl->last_io_jiffies);
+
+out_sleep:
+       wl1251_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct wl1251 *wl = hw->priv;
@@ -596,26 +683,14 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 
                wl->dtim_period = conf->ps_dtim_period;
 
-               ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
-                                                 wl->dtim_period);
-
-               /*
-                * mac80211 enables PSM only if we're already associated.
-                */
-               ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
-               if (ret < 0)
-                       goto out_sleep;
+               ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
        } else if (!(conf->flags & IEEE80211_CONF_PS) &&
                   wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm disabled");
 
                wl->psm_requested = false;
 
-               if (wl->station_mode != STATION_ACTIVE_MODE) {
-                       ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
-                       if (ret < 0)
-                               goto out_sleep;
-               }
+               ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_IDLE) {
@@ -1414,6 +1489,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
 
        skb_queue_head_init(&wl->tx_queue);
 
+       INIT_DELAYED_WORK(&wl->ps_work, wl1251_ps_work);
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->scanning = false;
index d258962..c449857 100644 (file)
@@ -178,6 +178,8 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode)
                break;
        }
        wl->station_mode = mode;
+       wl->ps_transitioning = true;
+       wl->ps_change_jiffies = jiffies;
 
        return ret;
 }
index 6af3526..c069669 100644 (file)
@@ -186,6 +186,8 @@ static void wl1251_rx_body(struct wl1251 *wl,
 
        if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
                beacon = 1;
+       else
+               wl->last_io_jiffies = jiffies;
 
        wl1251_rx_status(wl, desc, &status, beacon);
 
index 28121c5..73aa3cb 100644 (file)
@@ -523,6 +523,7 @@ void wl1251_tx_complete(struct wl1251 *wl)
        }
 
        wl->next_tx_complete = result_index;
+       wl->last_io_jiffies = jiffies;
 }
 
 /* caller must hold wl->mutex */
index 5c1ec78..8051cd4 100644 (file)
@@ -397,6 +397,13 @@ struct wl1251 {
        s8 noise;
 
        void *eeprom_dump;
+
+       /* PS hacks.. */
+       unsigned long ps_change_jiffies;
+       unsigned long last_io_jiffies;
+       struct delayed_work ps_work;
+       bool bss_lost;
+       bool ps_transitioning;
 };
 
 int wl1251_plt_start(struct wl1251 *wl);