mac80211: wait for beacon before enabling powersave
Johannes Berg [Tue, 26 Jan 2010 13:19:52 +0000 (14:19 +0100)]
Because DTIM information is required for powersave
but is only conveyed in beacons, wait for a beacon
before enabling powersave, and change the way the
information is conveyed to the driver accordingly.

mwl8k doesn't currently seem to implement PS but
requires the DTIM period in a different way; after
talking to Lennert we agreed to just have mwl8k do
the parsing itself in the finalize_join work.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

drivers/net/wireless/iwlwifi/iwl-power.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/wl12xx/wl1251.h
drivers/net/wireless/wl12xx/wl1251_main.c
include/net/mac80211.h
net/mac80211/mlme.c
net/mac80211/scan.c

index 8599444..9e3ca06 100644 (file)
@@ -319,7 +319,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
                        priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
 
        if (priv->vif)
-               dtimper = priv->vif->bss_conf.dtim_period;
+               dtimper = priv->hw->conf.ps_dtim_period;
        else
                dtimper = 1;
 
index 68546ca..f0f08f3 100644 (file)
@@ -3881,12 +3881,16 @@ static void mwl8k_finalize_join_worker(struct work_struct *work)
        struct mwl8k_priv *priv =
                container_of(work, struct mwl8k_priv, finalize_join_worker);
        struct sk_buff *skb = priv->beacon_skb;
-       struct mwl8k_vif *mwl8k_vif;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+       int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+       const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM,
+                                        mgmt->u.beacon.variable, len);
+       int dtim_period = 1;
+
+       if (tim && tim[1] >= 2)
+               dtim_period = tim[3];
 
-       mwl8k_vif = mwl8k_first_vif(priv);
-       if (mwl8k_vif != NULL)
-               mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len,
-                                       mwl8k_vif->vif->bss_conf.dtim_period);
+       mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period);
 
        dev_kfree_skb(skb);
        priv->beacon_skb = NULL;
index 6301578..37c61c1 100644 (file)
@@ -341,9 +341,6 @@ struct wl1251 {
        /* Are we currently scanning */
        bool scanning;
 
-       /* Our association ID */
-       u16 aid;
-
        /* Default key (for WEP) */
        u32 default_key;
 
index 595f0f9..a717dde 100644 (file)
@@ -617,10 +617,13 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 
                wl->psm_requested = true;
 
+               wl->dtim_period = conf->ps_dtim_period;
+
+               ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
+                                                 wl->dtim_period);
+
                /*
-                * We enter PSM only if we're already associated.
-                * If we're not, we'll enter it when joining an SSID,
-                * through the bss_info_changed() hook.
+                * mac80211 enables PSM only if we're already associated.
                 */
                ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
                if (ret < 0)
@@ -943,7 +946,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                                       struct ieee80211_bss_conf *bss_conf,
                                       u32 changed)
 {
-       enum wl1251_cmd_ps_mode mode;
        struct wl1251 *wl = hw->priv;
        struct sk_buff *beacon, *skb;
        int ret;
@@ -984,11 +986,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
        if (changed & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
-                       wl->dtim_period = bss_conf->dtim_period;
-
-                       ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
-                                                         wl->dtim_period);
-                       wl->aid = bss_conf->aid;
 
                        skb = ieee80211_pspoll_get(wl->hw, wl->vif);
                        if (!skb)
@@ -1001,17 +998,9 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                        if (ret < 0)
                                goto out_sleep;
 
-                       ret = wl1251_acx_aid(wl, wl->aid);
+                       ret = wl1251_acx_aid(wl, bss_conf->aid);
                        if (ret < 0)
                                goto out_sleep;
-
-                       /* If we want to go in PSM but we're not there yet */
-                       if (wl->psm_requested && !wl->psm) {
-                               mode = STATION_POWER_SAVE_MODE;
-                               ret = wl1251_ps_set_mode(wl, mode);
-                               if (ret < 0)
-                                       goto out_sleep;
-                       }
                } else {
                        /* use defaults when not associated */
                        wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
index f64402f..1e9c930 100644 (file)
@@ -186,7 +186,8 @@ enum ieee80211_bss_change {
  * @use_short_slot: use short slot time (only relevant for ERP);
  *     if the hardware cannot handle this it must set the
  *     IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
- * @dtim_period: num of beacons before the next DTIM, for PSM
+ * @dtim_period: num of beacons before the next DTIM, for beaconing,
+ *     not valid in station mode (cf. hw conf ps_dtim_period)
  * @timestamp: beacon timestamp
  * @beacon_int: beacon interval
  * @assoc_capability: capabilities taken from assoc resp
@@ -648,6 +649,9 @@ enum ieee80211_smps_mode {
  *     value will be only achievable between DTIM frames, the hardware
  *     needs to check for the multicast traffic bit in DTIM beacons.
  *     This variable is valid only when the CONF_PS flag is set.
+ * @ps_dtim_period: The DTIM period of the AP we're connected to, for use
+ *     in power saving. Power saving will not be enabled until a beacon
+ *     has been received and the DTIM period is known.
  * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the
  *     powersave documentation below. This variable is valid only when
  *     the CONF_PS flag is set.
@@ -674,6 +678,7 @@ struct ieee80211_conf {
        int max_sleep_period;
 
        u16 listen_interval;
+       u8 ps_dtim_period;
 
        u8 long_frame_max_tx_count, short_frame_max_tx_count;
 
index 1e1d16c..86c6ad1 100644 (file)
@@ -484,6 +484,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 
        if (count == 1 && found->u.mgd.powersave &&
            found->u.mgd.associated &&
+           found->u.mgd.associated->beacon_ies &&
            !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
                                    IEEE80211_STA_CONNECTION_POLL))) {
                s32 beaconint_us;
@@ -497,14 +498,22 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                if (beaconint_us > latency) {
                        local->ps_sdata = NULL;
                } else {
-                       u8 dtimper = found->vif.bss_conf.dtim_period;
+                       struct ieee80211_bss *bss;
                        int maxslp = 1;
+                       u8 dtimper;
 
-                       if (dtimper > 1)
+                       bss = (void *)found->u.mgd.associated->priv;
+                       dtimper = bss->dtim_period;
+
+                       /* If the TIM IE is invalid, pretend the value is 1 */
+                       if (!dtimper)
+                               dtimper = 1;
+                       else if (dtimper > 1)
                                maxslp = min_t(int, dtimper,
                                                    latency / beaconint_us);
 
                        local->hw.conf.max_sleep_period = maxslp;
+                       local->hw.conf.ps_dtim_period = dtimper;
                        local->ps_sdata = found;
                }
        } else {
@@ -702,7 +711,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        /* set timing information */
        sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
        sdata->vif.bss_conf.timestamp = cbss->tsf;
-       sdata->vif.bss_conf.dtim_period = bss->dtim_period;
 
        bss_info_changed |= BSS_CHANGED_BEACON_INT;
        bss_info_changed |= ieee80211_handle_bss_capability(sdata,
@@ -1168,6 +1176,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
+       bool need_ps = false;
+
+       if (sdata->u.mgd.associated) {
+               bss = (void *)sdata->u.mgd.associated->priv;
+               /* not previously set so we may need to recalc */
+               need_ps = !bss->dtim_period;
+       }
 
        if (elems->ds_params && elems->ds_params_len == 1)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1187,6 +1202,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        if (!sdata->u.mgd.associated)
                return;
 
+       if (need_ps) {
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local, -1);
+               mutex_unlock(&local->iflist_mtx);
+       }
+
        if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
            (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
                                                        ETH_ALEN) == 0)) {
index 9afe2f9..bc061f6 100644 (file)
@@ -111,10 +111,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                bss->dtim_period = tim_ie->dtim_period;
        }
 
-       /* set default value for buggy AP/no TIM element */
-       if (bss->dtim_period == 0)
-               bss->dtim_period = 1;
-
        bss->supp_rates_len = 0;
        if (elems->supp_rates) {
                clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;