mac80211: correct wext transmit power handler
authorJohannes Berg <johannes@sipsolutions.net>
Tue, 7 Apr 2009 13:22:28 +0000 (15:22 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 16 Apr 2009 14:39:08 +0000 (10:39 -0400)
Wext makes no assumptions about the contents of
data->txpower.fixed and data->txpower.value when
data->txpower.disabled is set, so do not update
the user-requested power level while disabling.

Also, when wext configures a really _fixed_ power
output [1], we should reject it instead of limiting it
to the regulatory constraint. If the user wants to set
a _limit_ [2] then we should honour that.

[1] iwconfig wlan0 txpower 20dBm fixed
[2] iwconfig wlan0 txpower 10dBm

This fixes
http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1942

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/main.c
net/mac80211/wext.c

index a6f1d8a..fbcbed6 100644 (file)
@@ -258,7 +258,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
                        (chan->max_power - local->power_constr_level) :
                        chan->max_power;
 
-       if (local->user_power_level)
+       if (local->user_power_level >= 0)
                power = min(power, local->user_power_level);
 
        if (local->hw.conf.power_level != power) {
index deb4ece..ce9115c 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;
+               }
+
+               reconf = true;
 
-       local->user_power_level = new_power_level;
-       if (local->hw.conf.power_level != new_power_level)
-               reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
+               /*
+                * 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;