memcgroup: temporarily revert swapoff mod
[powerpc.git] / net / mac80211 / ieee80211_ioctl.c
index 3c324c3..5024d37 100644 (file)
 
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
+#include "ieee80211_led.h"
 #include "ieee80211_rate.h"
 #include "wpa.h"
 #include "aes_ccm.h"
 
 
 static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
-                                   int idx, int alg, int set_tx_key,
-                                   const u8 *_key, size_t key_len)
+                                   int idx, int alg, int remove,
+                                   int set_tx_key, const u8 *_key,
+                                   size_t key_len)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        int ret = 0;
@@ -75,7 +77,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
                key = sta->key;
        }
 
-       if (alg == ALG_NONE) {
+       if (remove) {
                ieee80211_key_free(key);
                key = NULL;
        } else {
@@ -104,14 +106,14 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
                                    struct iw_point *data, char *extra)
 {
        struct ieee80211_sub_if_data *sdata;
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
-       if (local->user_space_mlme)
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
                return -EOPNOTSUPP;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                int ret = ieee80211_sta_set_extra_ie(dev, extra, data->length);
                if (ret)
                        return ret;
@@ -120,15 +122,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
                return 0;
        }
 
-       if (sdata->type == IEEE80211_IF_TYPE_AP) {
-               kfree(sdata->u.ap.generic_elem);
-               sdata->u.ap.generic_elem = kmalloc(data->length, GFP_KERNEL);
-               if (!sdata->u.ap.generic_elem)
-                       return -ENOMEM;
-               memcpy(sdata->u.ap.generic_elem, extra, data->length);
-               sdata->u.ap.generic_elem_len = data->length;
-               return ieee80211_if_config(dev);
-       }
        return -EOPNOTSUPP;
 }
 
@@ -226,6 +219,8 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
        IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
        IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
 
+       range->scan_capa |= IW_SCAN_CAPA_ESSID;
+
        return 0;
 }
 
@@ -237,7 +232,7 @@ static int ieee80211_ioctl_siwmode(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        int type;
 
-       if (sdata->type == IEEE80211_IF_TYPE_VLAN)
+       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
                return -EOPNOTSUPP;
 
        switch (*mode) {
@@ -254,7 +249,7 @@ static int ieee80211_ioctl_siwmode(struct net_device *dev,
                return -EINVAL;
        }
 
-       if (type == sdata->type)
+       if (type == sdata->vif.type)
                return 0;
        if (netif_running(dev))
                return -EBUSY;
@@ -273,7 +268,7 @@ static int ieee80211_ioctl_giwmode(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       switch (sdata->type) {
+       switch (sdata->vif.type) {
        case IEEE80211_IF_TYPE_AP:
                *mode = IW_MODE_MASTER;
                break;
@@ -314,13 +309,16 @@ int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq)
                            ((chan->chan == channel) || (chan->freq == freq))) {
                                local->oper_channel = chan;
                                local->oper_hw_mode = mode;
-                               set++;
+                               set = 1;
+                               break;
                        }
                }
+               if (set)
+                       break;
        }
 
        if (set) {
-               if (local->sta_scanning)
+               if (local->sta_sw_scanning)
                        ret = 0;
                else
                        ret = ieee80211_hw_config(local);
@@ -338,13 +336,13 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev,
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->type == IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
                sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
 
        /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
        if (freq->e == 0) {
                if (freq->m < 0) {
-                       if (sdata->type == IEEE80211_IF_TYPE_STA)
+                       if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
                                sdata->u.sta.flags |=
                                        IEEE80211_STA_AUTO_CHANNEL_SEL;
                        return 0;
@@ -382,7 +380,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
                                    struct iw_request_info *info,
                                    struct iw_point *data, char *ssid)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata;
        size_t len = data->length;
 
@@ -391,10 +388,10 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
                len--;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                int ret;
-               if (local->user_space_mlme) {
+               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
                        if (len > IEEE80211_MAX_SSID_LEN)
                                return -EINVAL;
                        memcpy(sdata->u.sta.ssid, ssid, len);
@@ -412,7 +409,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
                return 0;
        }
 
-       if (sdata->type == IEEE80211_IF_TYPE_AP) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
                memcpy(sdata->u.ap.ssid, ssid, len);
                memset(sdata->u.ap.ssid + len, 0,
                       IEEE80211_MAX_SSID_LEN - len);
@@ -431,8 +428,8 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
 
        struct ieee80211_sub_if_data *sdata;
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                int res = ieee80211_sta_get_ssid(dev, ssid, &len);
                if (res == 0) {
                        data->length = len;
@@ -442,7 +439,7 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
                return res;
        }
 
-       if (sdata->type == IEEE80211_IF_TYPE_AP) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
                len = sdata->u.ap.ssid_len;
                if (len > IW_ESSID_MAX_SIZE)
                        len = IW_ESSID_MAX_SIZE;
@@ -459,14 +456,13 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
                                 struct iw_request_info *info,
                                 struct sockaddr *ap_addr, char *extra)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                int ret;
-               if (local->user_space_mlme) {
+               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
                        memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
                               ETH_ALEN);
                        return 0;
@@ -483,7 +479,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
                        return ret;
                ieee80211_sta_req_auth(dev, &sdata->u.sta);
                return 0;
-       } else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
+       } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
                if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
                           ETH_ALEN) == 0)
                        return 0;
@@ -501,12 +497,12 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                ap_addr->sa_family = ARPHRD_ETHER;
                memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
                return 0;
-       } else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
+       } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
                ap_addr->sa_family = ARPHRD_ETHER;
                memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
                return 0;
@@ -518,32 +514,27 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
 
 static int ieee80211_ioctl_siwscan(struct net_device *dev,
                                   struct iw_request_info *info,
-                                  struct iw_point *data, char *extra)
+                                  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);
+       struct iw_scan_req *req = NULL;
        u8 *ssid = NULL;
        size_t ssid_len = 0;
 
        if (!netif_running(dev))
                return -ENETDOWN;
 
-       switch (sdata->type) {
-       case IEEE80211_IF_TYPE_STA:
-       case IEEE80211_IF_TYPE_IBSS:
-               if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
-                       ssid = sdata->u.sta.ssid;
-                       ssid_len = sdata->u.sta.ssid_len;
-               }
-               break;
-       case IEEE80211_IF_TYPE_AP:
-               if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) {
-                       ssid = sdata->u.ap.ssid;
-                       ssid_len = sdata->u.ap.ssid_len;
-               }
-               break;
-       default:
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
+           sdata->vif.type != IEEE80211_IF_TYPE_AP)
                return -EOPNOTSUPP;
+
+       /* if SSID was specified explicitly then use that */
+       if (wrqu->data.length == sizeof(struct iw_scan_req) &&
+           wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+               req = (struct iw_scan_req *)extra;
+               ssid = req->essid;
+               ssid_len = req->essid_len;
        }
 
        return ieee80211_sta_req_scan(dev, ssid, ssid_len);
@@ -556,8 +547,10 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
 {
        int res;
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       if (local->sta_scanning)
+
+       if (local->sta_sw_scanning || local->sta_hw_scanning)
                return -EAGAIN;
+
        res = ieee80211_sta_scan_results(dev, extra, data->length);
        if (res >= 0) {
                data->length = res;
@@ -589,7 +582,7 @@ static int ieee80211_ioctl_siwrate(struct net_device *dev,
        sdata->bss->force_unicast_rateidx = -1;
        if (rate->value < 0)
                return 0;
-       for (i=0; i< mode->num_rates; i++) {
+       for (i=0; i < mode->num_rates; i++) {
                struct ieee80211_rate *rates = &mode->rates[i];
                int this_rate = rates->rate;
 
@@ -597,10 +590,10 @@ static int ieee80211_ioctl_siwrate(struct net_device *dev,
                        sdata->bss->max_ratectrl_rateidx = i;
                        if (rate->fixed)
                                sdata->bss->force_unicast_rateidx = i;
-                       break;
+                       return 0;
                }
        }
-       return 0;
+       return -EINVAL;
 }
 
 static int ieee80211_ioctl_giwrate(struct net_device *dev,
@@ -612,7 +605,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
                sta = sta_info_get(local, sdata->u.sta.bssid);
        else
                return -EOPNOTSUPP;
@@ -632,22 +625,36 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        bool need_reconfig = 0;
+       u8 new_power_level;
 
        if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
                return -EINVAL;
        if (data->txpower.flags & IW_TXPOW_RANGE)
                return -EINVAL;
-       if (!data->txpower.fixed)
-               return -EINVAL;
 
-       if (local->hw.conf.power_level != data->txpower.value) {
-               local->hw.conf.power_level = data->txpower.value;
+       if (data->txpower.fixed) {
+               new_power_level = data->txpower.value;
+       } else {
+               /* Automatic power level. Get the px power from the current
+                * channel. */
+               struct ieee80211_channel* chan = local->oper_channel;
+               if (!chan)
+                       return -EINVAL;
+
+               new_power_level = chan->power_level;
+       }
+
+       if (local->hw.conf.power_level != new_power_level) {
+               local->hw.conf.power_level = new_power_level;
                need_reconfig = 1;
        }
+
        if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
                local->hw.conf.radio_enabled = !(data->txpower.disabled);
                need_reconfig = 1;
+               ieee80211_led_radio(local, local->hw.conf.radio_enabled);
        }
+
        if (need_reconfig) {
                ieee80211_hw_config(local);
                /* The return value of hw_config is not of big interest here,
@@ -812,8 +819,8 @@ static int ieee80211_ioctl_siwmlme(struct net_device *dev,
        struct iw_mlme *mlme = (struct iw_mlme *) extra;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type != IEEE80211_IF_TYPE_STA &&
-           sdata->type != IEEE80211_IF_TYPE_IBSS)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
                return -EINVAL;
 
        switch (mlme->cmd) {
@@ -836,6 +843,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
        int idx, i, alg = ALG_WEP;
        u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       int remove = 0;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -854,7 +862,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
                idx--;
 
        if (erq->flags & IW_ENCODE_DISABLED)
-               alg = ALG_NONE;
+               remove = 1;
        else if (erq->length == 0) {
                /* No key data - just set the default TX key index */
                ieee80211_set_default_key(sdata, idx);
@@ -863,7 +871,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
 
        return ieee80211_set_encryption(
                dev, bcaddr,
-               idx, alg,
+               idx, alg, remove,
                !sdata->default_key,
                keybuf, erq->length);
 }
@@ -914,7 +922,6 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
                                   struct iw_request_info *info,
                                   struct iw_param *data, char *extra)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        int ret = 0;
 
@@ -924,32 +931,33 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
        case IW_AUTH_CIPHER_GROUP:
        case IW_AUTH_WPA_ENABLED:
        case IW_AUTH_RX_UNENCRYPTED_EAPOL:
-               break;
        case IW_AUTH_KEY_MGMT:
-               if (sdata->type != IEEE80211_IF_TYPE_STA)
+               break;
+       case IW_AUTH_DROP_UNENCRYPTED:
+               sdata->drop_unencrypted = !!data->value;
+               break;
+       case IW_AUTH_PRIVACY_INVOKED:
+               if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                        ret = -EINVAL;
                else {
+                       sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
                        /*
-                        * Key management was set by wpa_supplicant,
-                        * we only need this to associate to a network
-                        * that has privacy enabled regardless of not
-                        * having a key.
+                        * Privacy invoked by wpa_supplicant, store the
+                        * value and allow associating to a protected
+                        * network without having a key up front.
                         */
-                       sdata->u.sta.key_management_enabled = !!data->value;
+                       if (data->value)
+                               sdata->u.sta.flags |=
+                                       IEEE80211_STA_PRIVACY_INVOKED;
                }
                break;
        case IW_AUTH_80211_AUTH_ALG:
-               if (sdata->type == IEEE80211_IF_TYPE_STA ||
-                   sdata->type == IEEE80211_IF_TYPE_IBSS)
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+                   sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
                        sdata->u.sta.auth_algs = data->value;
                else
                        ret = -EOPNOTSUPP;
                break;
-       case IW_AUTH_PRIVACY_INVOKED:
-               if (local->ops->set_privacy_invoked)
-                       ret = local->ops->set_privacy_invoked(
-                                       local_to_hw(local), data->value);
-               break;
        default:
                ret = -EOPNOTSUPP;
                break;
@@ -965,8 +973,8 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta = NULL;
 
-       if (sdata->type == IEEE80211_IF_TYPE_STA ||
-           sdata->type == IEEE80211_IF_TYPE_IBSS)
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
                sta = sta_info_get(local, sdata->u.sta.bssid);
        if (!sta) {
                wstats->discard.fragment = 0;
@@ -994,8 +1002,8 @@ static int ieee80211_ioctl_giwauth(struct net_device *dev,
 
        switch (data->flags & IW_AUTH_INDEX) {
        case IW_AUTH_80211_AUTH_ALG:
-               if (sdata->type == IEEE80211_IF_TYPE_STA ||
-                   sdata->type == IEEE80211_IF_TYPE_IBSS)
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+                   sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
                        data->value = sdata->u.sta.auth_algs;
                else
                        ret = -EOPNOTSUPP;
@@ -1014,11 +1022,11 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
-       int alg, idx, i;
+       int uninitialized_var(alg), idx, i, remove = 0;
 
        switch (ext->alg) {
        case IW_ENCODE_ALG_NONE:
-               alg = ALG_NONE;
+               remove = 1;
                break;
        case IW_ENCODE_ALG_WEP:
                alg = ALG_WEP;
@@ -1034,7 +1042,7 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
        }
 
        if (erq->flags & IW_ENCODE_DISABLED)
-               alg = ALG_NONE;
+               remove = 1;
 
        idx = erq->flags & IW_ENCODE_INDEX;
        if (idx < 1 || idx > 4) {
@@ -1053,6 +1061,7 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
                idx--;
 
        return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg,
+                                       remove,
                                        ext->ext_flags &
                                        IW_ENCODE_EXT_SET_TX_KEY,
                                        ext->key, ext->key_len);