]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/mlme.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[~andy/linux] / net / mac80211 / mlme.c
index ba2da11a997b2a0fa394bdf5a3f0e2bb6af944cd..341e18344679fbeb37980a2a4bbe7699671377fb 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
+#include <linux/moduleparam.h>
 #include <linux/rtnetlink.h>
 #include <linux/pm_qos.h>
 #include <linux/crc32.h>
 #include <linux/slab.h>
+#include <linux/export.h>
 #include <net/mac80211.h>
 #include <asm/unaligned.h>
 
@@ -637,6 +639,9 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
        if (!mgd->powersave)
                return false;
 
+       if (mgd->broken_ap)
+               return false;
+
        if (!mgd->associated)
                return false;
 
@@ -1463,6 +1468,47 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
        return RX_MGMT_CFG80211_DISASSOC;
 }
 
+static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
+                               u8 *supp_rates, unsigned int supp_rates_len,
+                               u32 *rates, u32 *basic_rates,
+                               bool *have_higher_than_11mbit,
+                               int *min_rate, int *min_rate_index)
+{
+       int i, j;
+
+       for (i = 0; i < supp_rates_len; i++) {
+               int rate = (supp_rates[i] & 0x7f) * 5;
+               bool is_basic = !!(supp_rates[i] & 0x80);
+
+               if (rate > 110)
+                       *have_higher_than_11mbit = true;
+
+               /*
+                * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009
+                * 7.3.2.2 as a magic value instead of a rate. Hence, skip it.
+                *
+                * Note: Even through the membership selector and the basic
+                *       rate flag share the same bit, they are not exactly
+                *       the same.
+                */
+               if (!!(supp_rates[i] & 0x80) &&
+                   (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+                       continue;
+
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       if (sband->bitrates[j].bitrate == rate) {
+                               *rates |= BIT(j);
+                               if (is_basic)
+                                       *basic_rates |= BIT(j);
+                               if (rate < *min_rate) {
+                                       *min_rate = rate;
+                                       *min_rate_index = j;
+                               }
+                               break;
+                       }
+               }
+       }
+}
 
 static bool ieee80211_assoc_success(struct ieee80211_work *wk,
                                    struct ieee80211_mgmt *mgmt, size_t len)
@@ -1479,9 +1525,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        struct ieee802_11_elems elems;
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        u32 changed = 0;
-       int i, j, err;
+       int err;
        bool have_higher_than_11mbit = false;
        u16 ap_ht_cap_flags;
+       int min_rate = INT_MAX, min_rate_index = -1;
 
        /* AssocResp and ReassocResp have identical structure */
 
@@ -1489,10 +1536,21 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
        if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
-               printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
-                      "set\n", sdata->name, aid);
+               printk(KERN_DEBUG
+                      "%s: invalid AID value 0x%x; bits 15:14 not set\n",
+                      sdata->name, aid);
        aid &= ~(BIT(15) | BIT(14));
 
+       ifmgd->broken_ap = false;
+
+       if (aid == 0 || aid > IEEE80211_MAX_AID) {
+               printk(KERN_DEBUG
+                      "%s: invalid AID value %d (out of range), turn off PS\n",
+                      sdata->name, aid);
+               aid = 0;
+               ifmgd->broken_ap = true;
+       }
+
        pos = mgmt->u.assoc_resp.variable;
        ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
 
@@ -1525,38 +1583,23 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        basic_rates = 0;
        sband = local->hw.wiphy->bands[wk->chan->band];
 
-       for (i = 0; i < elems.supp_rates_len; i++) {
-               int rate = (elems.supp_rates[i] & 0x7f) * 5;
-               bool is_basic = !!(elems.supp_rates[i] & 0x80);
-
-               if (rate > 110)
-                       have_higher_than_11mbit = true;
-
-               for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
-                               rates |= BIT(j);
-                               if (is_basic)
-                                       basic_rates |= BIT(j);
-                               break;
-                       }
-               }
-       }
+       ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len,
+                           &rates, &basic_rates, &have_higher_than_11mbit,
+                           &min_rate, &min_rate_index);
 
-       for (i = 0; i < elems.ext_supp_rates_len; i++) {
-               int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
-               bool is_basic = !!(elems.ext_supp_rates[i] & 0x80);
+       ieee80211_get_rates(sband, elems.ext_supp_rates,
+                           elems.ext_supp_rates_len, &rates, &basic_rates,
+                           &have_higher_than_11mbit,
+                           &min_rate, &min_rate_index);
 
-               if (rate > 110)
-                       have_higher_than_11mbit = true;
-
-               for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate) {
-                               rates |= BIT(j);
-                               if (is_basic)
-                                       basic_rates |= BIT(j);
-                               break;
-                       }
-               }
+       /*
+        * some buggy APs don't advertise basic_rates. use the lowest
+        * supported rate instead.
+        */
+       if (unlikely(!basic_rates) && min_rate_index >= 0) {
+               printk(KERN_DEBUG "%s: No basic rates in AssocResp. "
+                      "Using min supported rate instead.\n", sdata->name);
+               basic_rates = BIT(min_rate_index);
        }
 
        sta->sta.supp_rates[wk->chan->band] = rates;