mac80211: improve powersave implementation
[pandora-kernel.git] / net / mac80211 / wext.c
index 7162d58..81f63e5 100644 (file)
@@ -37,7 +37,14 @@ static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta
        struct ieee80211_key *key;
        int err;
 
-       if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
+       if (alg == ALG_AES_CMAC) {
+               if (idx < NUM_DEFAULT_KEYS ||
+                   idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) {
+                       printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d "
+                              "(BIP)\n", sdata->dev->name, idx);
+                       return -EINVAL;
+               }
+       } else if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
                printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
                       sdata->dev->name, idx);
                return -EINVAL;
@@ -103,6 +110,9 @@ static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta
 
                if (set_tx_key || (!sta && !sdata->default_key && key))
                        ieee80211_set_default_key(sdata, idx);
+               if (alg == ALG_AES_CMAC &&
+                   (set_tx_key || (!sta && !sdata->default_mgmt_key && key)))
+                       ieee80211_set_default_mgmt_key(sdata, idx);
        }
 
  out_unlock:
@@ -119,125 +129,38 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
-               return -EOPNOTSUPP;
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
                if (ret)
                        return ret;
-               sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
-               ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+               sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
+               ieee80211_sta_req_auth(sdata);
                return 0;
        }
 
        return -EOPNOTSUPP;
 }
 
-static int ieee80211_ioctl_giwrange(struct net_device *dev,
-                                struct iw_request_info *info,
-                                struct iw_point *data, char *extra)
-{
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct iw_range *range = (struct iw_range *) extra;
-       enum ieee80211_band band;
-       int c = 0;
-
-       data->length = sizeof(struct iw_range);
-       memset(range, 0, sizeof(struct iw_range));
-
-       range->we_version_compiled = WIRELESS_EXT;
-       range->we_version_source = 21;
-       range->retry_capa = IW_RETRY_LIMIT;
-       range->retry_flags = IW_RETRY_LIMIT;
-       range->min_retry = 0;
-       range->max_retry = 255;
-       range->min_rts = 0;
-       range->max_rts = 2347;
-       range->min_frag = 256;
-       range->max_frag = 2346;
-
-       range->encoding_size[0] = 5;
-       range->encoding_size[1] = 13;
-       range->num_encoding_sizes = 2;
-       range->max_encoding_tokens = NUM_DEFAULT_KEYS;
-
-       if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC ||
-           local->hw.flags & IEEE80211_HW_SIGNAL_DB)
-               range->max_qual.level = local->hw.max_signal;
-       else if  (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
-               range->max_qual.level = -110;
-       else
-               range->max_qual.level = 0;
-
-       if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
-               range->max_qual.noise = -110;
-       else
-               range->max_qual.noise = 0;
-
-       range->max_qual.qual = 100;
-       range->max_qual.updated = local->wstats_flags;
-
-       range->avg_qual.qual = 50;
-       /* not always true but better than nothing */
-       range->avg_qual.level = range->max_qual.level / 2;
-       range->avg_qual.noise = range->max_qual.noise / 2;
-       range->avg_qual.updated = local->wstats_flags;
-
-       range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
-                         IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
-
-       for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
-               int i;
-               struct ieee80211_supported_band *sband;
-
-               sband = local->hw.wiphy->bands[band];
-
-               if (!sband)
-                       continue;
-
-               for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) {
-                       struct ieee80211_channel *chan = &sband->channels[i];
-
-                       if (!(chan->flags & IEEE80211_CHAN_DISABLED)) {
-                               range->freq[c].i =
-                                       ieee80211_frequency_to_channel(
-                                               chan->center_freq);
-                               range->freq[c].m = chan->center_freq;
-                               range->freq[c].e = 6;
-                               c++;
-                       }
-               }
-       }
-       range->num_channels = c;
-       range->num_frequency = c;
-
-       IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
-       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;
-}
-
-
 static int ieee80211_ioctl_siwfreq(struct net_device *dev,
                                   struct iw_request_info *info,
                                   struct iw_freq *freq, char *extra)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION)
-               sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+       else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               sdata->u.mgd.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->vif.type == NL80211_IFTYPE_STATION)
-                               sdata->u.sta.flags |=
+                       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                               sdata->u.ibss.flags |=
+                                       IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+                       else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                               sdata->u.mgd.flags |=
                                        IEEE80211_STA_AUTO_CHANNEL_SEL;
                        return 0;
                } else
@@ -274,32 +197,28 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
 {
        struct ieee80211_sub_if_data *sdata;
        size_t len = data->length;
+       int ret;
 
        /* iwconfig uses nul termination in SSID.. */
        if (len > 0 && ssid[len - 1] == '\0')
                len--;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-               int ret;
-               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
-                       if (len > IEEE80211_MAX_SSID_LEN)
-                               return -EINVAL;
-                       memcpy(sdata->u.sta.ssid, ssid, len);
-                       sdata->u.sta.ssid_len = len;
-                       return 0;
-               }
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                if (data->flags)
-                       sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+                       sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
                else
-                       sdata->u.sta.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+                       sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+
                ret = ieee80211_sta_set_ssid(sdata, ssid, len);
                if (ret)
                        return ret;
-               ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
+               ieee80211_sta_req_auth(sdata);
                return 0;
-       }
+       } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               return ieee80211_ibss_set_ssid(sdata, ssid, len);
 
        return -EOPNOTSUPP;
 }
@@ -313,8 +232,7 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
 
        struct ieee80211_sub_if_data *sdata;
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
                if (res == 0) {
                        data->length = len;
@@ -322,6 +240,14 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
                } else
                        data->flags = 0;
                return res;
+       } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               int res = ieee80211_ibss_get_ssid(sdata, ssid, &len);
+               if (res == 0) {
+                       data->length = len;
+                       data->flags = 1;
+               } else
+                       data->flags = 0;
+               return res;
        }
 
        return -EOPNOTSUPP;
@@ -335,26 +261,32 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                int ret;
-               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
-                       memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
-                              ETH_ALEN);
-                       return 0;
-               }
+
                if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
-                       sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
+                       sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
                                IEEE80211_STA_AUTO_CHANNEL_SEL;
                else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
-                       sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
+                       sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
                else
-                       sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+                       sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
                ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
                if (ret)
                        return ret;
-               ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
+               ieee80211_sta_req_auth(sdata);
                return 0;
+       } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
+                       sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL |
+                                              IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+               else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
+                       sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL;
+               else
+                       sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_BSSID_SEL;
+
+               return ieee80211_ibss_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
        } else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
                /*
                 * If it is necessary to update the WDS peer address
@@ -383,17 +315,20 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-               if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATED ||
-                   sdata->u.sta.state == IEEE80211_STA_MLME_IBSS_JOINED) {
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+               if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) {
                        ap_addr->sa_family = ARPHRD_ETHER;
-                       memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
-                       return 0;
-               } else {
+                       memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN);
+               } else
                        memset(&ap_addr->sa_data, 0, ETH_ALEN);
-                       return 0;
-               }
+               return 0;
+       } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               if (sdata->u.ibss.state == IEEE80211_IBSS_MLME_JOINED) {
+                       ap_addr->sa_family = ARPHRD_ETHER;
+                       memcpy(&ap_addr->sa_data, sdata->u.ibss.bssid, ETH_ALEN);
+               } else
+                       memset(&ap_addr->sa_data, 0, ETH_ALEN);
+               return 0;
        } else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
                ap_addr->sa_family = ARPHRD_ETHER;
                memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
@@ -404,58 +339,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
 }
 
 
-static int ieee80211_ioctl_siwscan(struct net_device *dev,
-                                  struct iw_request_info *info,
-                                  union iwreq_data *wrqu, char *extra)
-{
-       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;
-
-       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
-           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               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_request_scan(sdata, ssid, ssid_len);
-}
-
-
-static int ieee80211_ioctl_giwscan(struct net_device *dev,
-                                  struct iw_request_info *info,
-                                  struct iw_point *data, char *extra)
-{
-       int res;
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       struct ieee80211_sub_if_data *sdata;
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       if (local->sw_scanning || local->hw_scanning)
-               return -EAGAIN;
-
-       res = ieee80211_scan_results(local, info, extra, data->length);
-       if (res >= 0) {
-               data->length = res;
-               return 0;
-       }
-       data->length = 0;
-       return res;
-}
-
-
 static int ieee80211_ioctl_siwrate(struct net_device *dev,
                                  struct iw_request_info *info,
                                  struct iw_param *rate, char *extra)
@@ -511,7 +394,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, sdata->u.sta.bssid);
+       sta = sta_info_get(local, sdata->u.mgd.bssid);
 
        if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
                rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
@@ -534,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;
 
@@ -544,14 +428,37 @@ 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;
 
-       if (local->hw.conf.power_level != new_power_level) {
-               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)) {
@@ -560,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;
@@ -648,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;
@@ -713,8 +620,7 @@ 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->vif.type != NL80211_IFTYPE_STATION &&
-           sdata->vif.type != NL80211_IFTYPE_ADHOC)
+       if (!(sdata->vif.type == NL80211_IFTYPE_STATION))
                return -EINVAL;
 
        switch (mlme->cmd) {
@@ -737,7 +643,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;
+       int remove = 0, ret;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -763,11 +669,20 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
                return 0;
        }
 
-       return ieee80211_set_encryption(
+       ret = ieee80211_set_encryption(
                sdata, bcaddr,
                idx, alg, remove,
                !sdata->default_key,
                keybuf, erq->length);
+
+       if (!ret && sdata->vif.type == NL80211_IFTYPE_STATION) {
+               if (remove)
+                       sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
+               else
+                       sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED;
+       }
+
+       return ret;
 }
 
 
@@ -810,8 +725,7 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev,
        erq->flags |= IW_ENCODE_ENABLED;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-               switch (ifsta->auth_alg) {
+               switch (sdata->u.mgd.auth_alg) {
                case WLAN_AUTH_OPEN:
                case WLAN_AUTH_LEAP:
                        erq->flags |= IW_ENCODE_OPEN;
@@ -833,9 +747,12 @@ 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))
+               return -EOPNOTSUPP;
+
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
@@ -852,34 +769,29 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
                ps = true;
                break;
        default:                /* Otherwise we ignore */
-               break;
+               return -EINVAL;
        }
 
+       if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT))
+               return -EINVAL;
+
        if (wrq->flags & IW_POWER_TIMEOUT)
                timeout = wrq->value / 1000;
 
-set:
-       if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
-               return ret;
+ set:
+       if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
+               return 0;
 
-       local->powersave = ps;
-       local->dynamic_ps_timeout = timeout;
+       sdata->u.mgd.powersave = ps;
+       conf->dynamic_ps_timeout = timeout;
 
-       if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
-               if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
-                   local->dynamic_ps_timeout > 0)
-                       mod_timer(&local->dynamic_ps_timer, jiffies +
-                                 msecs_to_jiffies(local->dynamic_ps_timeout));
-               else {
-                       if (local->powersave)
-                               conf->flags |= IEEE80211_CONF_PS;
-                       else
-                               conf->flags &= ~IEEE80211_CONF_PS;
-               }
-               ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-       }
+       if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+               ieee80211_hw_config(local,
+                                   IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
 
-       return ret;
+       ieee80211_recalc_ps(local);
+
+       return 0;
 }
 
 static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -887,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;
 }
@@ -903,11 +815,22 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
 
        switch (data->flags & IW_AUTH_INDEX) {
        case IW_AUTH_WPA_VERSION:
-       case IW_AUTH_CIPHER_PAIRWISE:
        case IW_AUTH_CIPHER_GROUP:
        case IW_AUTH_WPA_ENABLED:
        case IW_AUTH_RX_UNENCRYPTED_EAPOL:
        case IW_AUTH_KEY_MGMT:
+       case IW_AUTH_CIPHER_GROUP_MGMT:
+               break;
+       case IW_AUTH_CIPHER_PAIRWISE:
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       if (data->value & (IW_AUTH_CIPHER_WEP40 |
+                           IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP))
+                               sdata->u.mgd.flags |=
+                                       IEEE80211_STA_TKIP_WEP_USED;
+                       else
+                               sdata->u.mgd.flags &=
+                                       ~IEEE80211_STA_TKIP_WEP_USED;
+               }
                break;
        case IW_AUTH_DROP_UNENCRYPTED:
                sdata->drop_unencrypted = !!data->value;
@@ -916,24 +839,45 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
                if (sdata->vif.type != NL80211_IFTYPE_STATION)
                        ret = -EINVAL;
                else {
-                       sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
+                       sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
                        /*
                         * Privacy invoked by wpa_supplicant, store the
                         * value and allow associating to a protected
                         * network without having a key up front.
                         */
                        if (data->value)
-                               sdata->u.sta.flags |=
+                               sdata->u.mgd.flags |=
                                        IEEE80211_STA_PRIVACY_INVOKED;
                }
                break;
        case IW_AUTH_80211_AUTH_ALG:
-               if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC)
-                       sdata->u.sta.auth_algs = data->value;
+               if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                       sdata->u.mgd.auth_algs = data->value;
                else
                        ret = -EOPNOTSUPP;
                break;
+       case IW_AUTH_MFP:
+               if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) {
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       switch (data->value) {
+                       case IW_AUTH_MFP_DISABLED:
+                               sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED;
+                               break;
+                       case IW_AUTH_MFP_OPTIONAL:
+                               sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL;
+                               break;
+                       case IW_AUTH_MFP_REQUIRED:
+                               sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED;
+                               break;
+                       default:
+                               ret = -EINVAL;
+                       }
+               } else
+                       ret = -EOPNOTSUPP;
+               break;
        default:
                ret = -EOPNOTSUPP;
                break;
@@ -951,9 +895,9 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
 
        rcu_read_lock();
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-           sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               sta = sta_info_get(local, sdata->u.sta.bssid);
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               sta = sta_info_get(local, sdata->u.mgd.bssid);
+
        if (!sta) {
                wstats->discard.fragment = 0;
                wstats->discard.misc = 0;
@@ -962,10 +906,45 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
                wstats->qual.noise = 0;
                wstats->qual.updated = IW_QUAL_ALL_INVALID;
        } else {
-               wstats->qual.level = sta->last_signal;
-               wstats->qual.qual = sta->last_qual;
-               wstats->qual.noise = sta->last_noise;
-               wstats->qual.updated = local->wstats_flags;
+               wstats->qual.updated = 0;
+               /*
+                * mirror what cfg80211 does for iwrange/scan results,
+                * otherwise userspace gets confused.
+                */
+               if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
+                                      IEEE80211_HW_SIGNAL_DBM)) {
+                       wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED;
+                       wstats->qual.updated |= IW_QUAL_QUAL_UPDATED;
+               } else {
+                       wstats->qual.updated |= IW_QUAL_LEVEL_INVALID;
+                       wstats->qual.updated |= IW_QUAL_QUAL_INVALID;
+               }
+
+               if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
+                       wstats->qual.level = sta->last_signal;
+                       wstats->qual.qual = sta->last_signal;
+               } else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+                       int sig = sta->last_signal;
+
+                       wstats->qual.updated |= IW_QUAL_DBM;
+                       wstats->qual.level = sig;
+                       if (sig < -110)
+                               sig = -110;
+                       else if (sig > -40)
+                               sig = -40;
+                       wstats->qual.qual = sig + 110;
+               }
+
+               if (local->hw.flags & IEEE80211_HW_NOISE_DBM) {
+                       /*
+                        * This assumes that if driver reports noise, it also
+                        * reports signal in dBm.
+                        */
+                       wstats->qual.noise = sta->last_noise;
+                       wstats->qual.updated |= IW_QUAL_NOISE_UPDATED;
+               } else {
+                       wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
+               }
        }
 
        rcu_read_unlock();
@@ -982,9 +961,8 @@ static int ieee80211_ioctl_giwauth(struct net_device *dev,
 
        switch (data->flags & IW_AUTH_INDEX) {
        case IW_AUTH_80211_AUTH_ALG:
-               if (sdata->vif.type == NL80211_IFTYPE_STATION ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC)
-                       data->value = sdata->u.sta.auth_algs;
+               if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                       data->value = sdata->u.mgd.auth_algs;
                else
                        ret = -EOPNOTSUPP;
                break;
@@ -1017,6 +995,9 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
        case IW_ENCODE_ALG_CCMP:
                alg = ALG_CCMP;
                break;
+       case IW_ENCODE_ALG_AES_CMAC:
+               alg = ALG_AES_CMAC;
+               break;
        default:
                return -EOPNOTSUPP;
        }
@@ -1025,20 +1006,41 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
                remove = 1;
 
        idx = erq->flags & IW_ENCODE_INDEX;
-       if (idx < 1 || idx > 4) {
-               idx = -1;
-               if (!sdata->default_key)
-                       idx = 0;
-               else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
-                       if (sdata->default_key == sdata->keys[i]) {
-                               idx = i;
-                               break;
+       if (alg == ALG_AES_CMAC) {
+               if (idx < NUM_DEFAULT_KEYS + 1 ||
+                   idx > NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) {
+                       idx = -1;
+                       if (!sdata->default_mgmt_key)
+                               idx = 0;
+                       else for (i = NUM_DEFAULT_KEYS;
+                                 i < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS;
+                                 i++) {
+                               if (sdata->default_mgmt_key == sdata->keys[i])
+                               {
+                                       idx = i;
+                                       break;
+                               }
                        }
-               }
-               if (idx < 0)
-                       return -EINVAL;
-       } else
-               idx--;
+                       if (idx < 0)
+                               return -EINVAL;
+               } else
+                       idx--;
+       } else {
+               if (idx < 1 || idx > 4) {
+                       idx = -1;
+                       if (!sdata->default_key)
+                               idx = 0;
+                       else for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+                               if (sdata->default_key == sdata->keys[i]) {
+                                       idx = i;
+                                       break;
+                               }
+                       }
+                       if (idx < 0)
+                               return -EINVAL;
+               } else
+                       idx--;
+       }
 
        return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg,
                                        remove,
@@ -1063,7 +1065,7 @@ static const iw_handler ieee80211_handler[] =
        (iw_handler) NULL,                              /* SIOCSIWSENS */
        (iw_handler) NULL,                              /* SIOCGIWSENS */
        (iw_handler) NULL /* not used */,               /* SIOCSIWRANGE */
-       (iw_handler) ieee80211_ioctl_giwrange,          /* SIOCGIWRANGE */
+       (iw_handler) cfg80211_wext_giwrange,            /* SIOCGIWRANGE */
        (iw_handler) NULL /* not used */,               /* SIOCSIWPRIV */
        (iw_handler) NULL /* kernel code */,            /* SIOCGIWPRIV */
        (iw_handler) NULL /* not used */,               /* SIOCSIWSTATS */
@@ -1076,8 +1078,8 @@ static const iw_handler ieee80211_handler[] =
        (iw_handler) ieee80211_ioctl_giwap,             /* SIOCGIWAP */
        (iw_handler) ieee80211_ioctl_siwmlme,           /* SIOCSIWMLME */
        (iw_handler) NULL,                              /* SIOCGIWAPLIST */
-       (iw_handler) ieee80211_ioctl_siwscan,           /* SIOCSIWSCAN */
-       (iw_handler) ieee80211_ioctl_giwscan,           /* SIOCGIWSCAN */
+       (iw_handler) cfg80211_wext_siwscan,             /* SIOCSIWSCAN */
+       (iw_handler) cfg80211_wext_giwscan,             /* SIOCGIWSCAN */
        (iw_handler) ieee80211_ioctl_siwessid,          /* SIOCSIWESSID */
        (iw_handler) ieee80211_ioctl_giwessid,          /* SIOCGIWESSID */
        (iw_handler) NULL,                              /* SIOCSIWNICKN */