Merge branch 'char-misc-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[pandora-kernel.git] / net / mac80211 / mlme.c
index ba2da11..b1b1bb3 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;
 
@@ -1482,6 +1487,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        int i, j, 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 +1495,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);
 
@@ -1537,6 +1554,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
                                rates |= BIT(j);
                                if (is_basic)
                                        basic_rates |= BIT(j);
+                               if (rate < min_rate) {
+                                       min_rate = rate;
+                                       min_rate_index = j;
+                               }
                                break;
                        }
                }
@@ -1554,11 +1575,25 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
                                rates |= BIT(j);
                                if (is_basic)
                                        basic_rates |= BIT(j);
+                               if (rate < min_rate) {
+                                       min_rate = rate;
+                                       min_rate_index = 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;
        sdata->vif.bss_conf.basic_rates = basic_rates;
 
@@ -2253,6 +2288,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
 
        cancel_work_sync(&ifmgd->request_smps_work);
 
+       cancel_work_sync(&ifmgd->monitor_work);
        cancel_work_sync(&ifmgd->beacon_connection_loss_work);
        if (del_timer_sync(&ifmgd->timer))
                set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
@@ -2261,7 +2297,6 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
        if (del_timer_sync(&ifmgd->chswitch_timer))
                set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
 
-       cancel_work_sync(&ifmgd->monitor_work);
        /* these will just be re-established on connection */
        del_timer_sync(&ifmgd->conn_mon_timer);
        del_timer_sync(&ifmgd->bcn_mon_timer);