mac80211: improve powersave implementation
[pandora-kernel.git] / net / mac80211 / wext.c
index deb4ece..81f63e5 100644 (file)
@@ -417,6 +417,7 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_channel* chan = local->hw.conf.channel;
+       bool reconf = false;
        u32 reconf_flags = 0;
        int new_power_level;
 
@@ -427,14 +428,38 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
        if (!chan)
                return -EINVAL;
 
-       if (data->txpower.fixed)
-               new_power_level = min(data->txpower.value, chan->max_power);
-       else /* Automatic power level setting */
-               new_power_level = chan->max_power;
+       /* only change when not disabling */
+       if (!data->txpower.disabled) {
+               if (data->txpower.fixed) {
+                       if (data->txpower.value < 0)
+                               return -EINVAL;
+                       new_power_level = data->txpower.value;
+                       /*
+                        * Debatable, but we cannot do a fixed power
+                        * level above the regulatory constraint.
+                        * Use "iwconfig wlan0 txpower 15dBm" instead.
+                        */
+                       if (new_power_level > chan->max_power)
+                               return -EINVAL;
+               } else {
+                       /*
+                        * Automatic power level setting, max being the value
+                        * passed in from userland.
+                        */
+                       if (data->txpower.value < 0)
+                               new_power_level = -1;
+                       else
+                               new_power_level = data->txpower.value;
+               }
 
-       local->user_power_level = new_power_level;
-       if (local->hw.conf.power_level != new_power_level)
-               reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
+               reconf = true;
+
+               /*
+                * ieee80211_hw_config() will limit to the channel's
+                * max power and possibly power constraint from AP.
+                */
+               local->user_power_level = new_power_level;
+       }
 
        if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
                local->hw.conf.radio_enabled = !(data->txpower.disabled);
@@ -442,7 +467,7 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
                ieee80211_led_radio(local, local->hw.conf.radio_enabled);
        }
 
-       if (reconf_flags)
+       if (reconf || reconf_flags)
                ieee80211_hw_config(local, reconf_flags);
 
        return 0;
@@ -530,7 +555,7 @@ static int ieee80211_ioctl_giwfrag(struct net_device *dev,
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
        frag->value = local->fragmentation_threshold;
-       frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD);
+       frag->disabled = (frag->value >= IEEE80211_MAX_FRAG_THRESHOLD);
        frag->fixed = 1;
 
        return 0;
@@ -650,7 +675,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
                !sdata->default_key,
                keybuf, erq->length);
 
-       if (!ret) {
+       if (!ret && sdata->vif.type == NL80211_IFTYPE_STATION) {
                if (remove)
                        sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
                else
@@ -722,7 +747,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_conf *conf = &local->hw.conf;
-       int ret = 0, timeout = 0;
+       int timeout = 0;
        bool ps;
 
        if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -754,42 +779,19 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
                timeout = wrq->value / 1000;
 
  set:
-       if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
-               return ret;
+       if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
+               return 0;
 
-       local->powersave = ps;
+       sdata->u.mgd.powersave = ps;
        conf->dynamic_ps_timeout = timeout;
 
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-               ret = ieee80211_hw_config(local,
-                                         IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
+               ieee80211_hw_config(local,
+                                   IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-       if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
-               return ret;
+       ieee80211_recalc_ps(local);
 
-       if (conf->dynamic_ps_timeout > 0 &&
-           !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
-               mod_timer(&local->dynamic_ps_timer, jiffies +
-                         msecs_to_jiffies(conf->dynamic_ps_timeout));
-       } else {
-               if (local->powersave) {
-                       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-                               ieee80211_send_nullfunc(local, sdata, 1);
-                       conf->flags |= IEEE80211_CONF_PS;
-                       ret = ieee80211_hw_config(local,
-                                       IEEE80211_CONF_CHANGE_PS);
-               } else {
-                       conf->flags &= ~IEEE80211_CONF_PS;
-                       ret = ieee80211_hw_config(local,
-                                       IEEE80211_CONF_CHANGE_PS);
-                       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-                               ieee80211_send_nullfunc(local, sdata, 0);
-                       del_timer_sync(&local->dynamic_ps_timer);
-                       cancel_work_sync(&local->dynamic_ps_enable_work);
-               }
-       }
-
-       return ret;
+       return 0;
 }
 
 static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -797,9 +799,9 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
                                    union iwreq_data *wrqu,
                                    char *extra)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       wrqu->power.disabled = !local->powersave;
+       wrqu->power.disabled = !sdata->u.mgd.powersave;
 
        return 0;
 }