CHROMIUMOS: mac80211: Salvage BSS info for botched HT Association
authorPaul Stewart <pstew@chromium.org>
Thu, 10 May 2012 01:23:33 +0000 (01:23 +0000)
committerOlof Johansson <olof@lixom.net>
Fri, 1 Jun 2012 07:03:33 +0000 (00:03 -0700)
The mac80211 flags errors in the HT Information IE if the channel
information does not match the associated channel.  Unfortunately
in this condition nothing in the HT information element can be
trusted.  However, at this point we have already signalled to the
AP in our association request that we wanted to do HT.

As a workaround to these broken APs, we should try to see if the
beacon/probe response of the AP fared any better than the association
response.  If it did, we can use that to at least configure our
receive side so we will hopefully not lose downstream throughput
due to the AP trying bitrates that we cannot receive.

This change copies the HT Information IE into the ieee80211_bss
structure so it can be retrieved in ieee80211_enable_ht during
association.  We can then use this data as a backup to the
association response IEs and configure our receive channel
appropriately.

Signed-off-by: Paul Stewart <pstew@chromium.org>
BUG=chromium-os:25483
TEST=Associate to known offending AP and ensure that the correct
message appears in the logs.  Ensure that pages load quickly
after association to such APs.

Change-Id: Ib26a607d7dccf0334cc987e720261d9b450a538b
Reviewed-on: https://gerrit.chromium.org/gerrit/19405
Reviewed-by: Gary Morain <gmorain@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/scan.c

index 4b53162..28fe83e 100644 (file)
@@ -106,6 +106,9 @@ struct ieee80211_bss {
        bool has_erp_value;
        u8 erp_value;
 
+       bool has_ht_info;
+       struct ieee80211_ht_info ht_info;
+
        /* Keep track of the corruption of the last beacon/probe response. */
        u8 corrupt_data;
 
@@ -132,6 +135,7 @@ enum ieee80211_bss_corrupt_data_flags {
  * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE
  * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE
  * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE
+ * @IEEE80211_BSS_VALID_HT: HT information was gathered from non-corrupt IE
  *
  * These are bss flags that are attached to a bss in the
  * @valid_data field of &struct ieee80211_bss.  They show which parts
@@ -142,7 +146,8 @@ enum ieee80211_bss_valid_data_flags {
        IEEE80211_BSS_VALID_DTIM                = BIT(0),
        IEEE80211_BSS_VALID_WMM                 = BIT(1),
        IEEE80211_BSS_VALID_RATES               = BIT(2),
-       IEEE80211_BSS_VALID_ERP                 = BIT(3)
+       IEEE80211_BSS_VALID_ERP                 = BIT(3),
+       IEEE80211_BSS_VALID_HT                  = BIT(4)
 };
 
 static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss)
index d1f2e8c..5d1e04c 100644 (file)
@@ -177,6 +177,15 @@ static int ecw2cw(int ecw)
        return (1 << ecw) - 1;
 }
 
+static bool ieee80211_bss_get_htinfo(struct ieee80211_bss *bss,
+                                    struct ieee80211_ht_info *hti) {
+       if (bss == NULL || !bss->has_ht_info)
+               return false;
+
+       memcpy(hti, &bss->ht_info, sizeof(*hti));
+       return true;
+}
+
 /*
  * ieee80211_enable_ht should be called only after the operating band
  * has been determined as ht configuration depends on the hw's
@@ -185,7 +194,8 @@ static int ecw2cw(int ecw)
 static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                               struct ieee80211_ht_info *hti,
                               const u8 *bssid, u16 ap_ht_cap_flags,
-                              bool beacon_htcap_ie)
+                              bool beacon_htcap_ie,
+                              struct ieee80211_bss *bss)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
@@ -194,9 +204,11 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
        int hti_cfreq;
        u16 ht_opmode;
        bool enable_ht = true;
+       bool disable_tx_ht = false;
        enum nl80211_channel_type prev_chantype;
        enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
        enum nl80211_channel_type tx_channel_type;
+       struct ieee80211_ht_info bss_hti;
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
        prev_chantype = sdata->vif.bss_conf.channel_type;
@@ -210,16 +222,45 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                 * Netgear WNDR3700 sometimes reports 4 higher than
                 * the actual channel, for instance.
                 */
-               printk(KERN_DEBUG
-                      "%s: Wrong control channel in association"
-                      " response: configured center-freq: %d"
-                      " hti-cfreq: %d  hti->control_chan: %d"
-                      " band: %d.  Disabling HT.\n",
-                      sdata->name,
-                      local->hw.conf.channel->center_freq,
-                      hti_cfreq, hti->control_chan,
-                      sband->band);
-               enable_ht = false;
+               if (ieee80211_bss_get_htinfo(bss, &bss_hti) &&
+                   local->hw.conf.channel->center_freq ==
+                   ieee80211_channel_to_frequency(
+                          bss_hti.control_chan, sband->band)) {
+                       /*
+                        * If we disable HT overall, then we
+                        * will have associated saying we can
+                        * receive HT40, but then turned
+                        * around and become deaf to it.  Use
+                        * the HT information from the beacon
+                        * to configure receive, but restrain
+                        * ourselves from ever transmitting in
+                        * HT.
+                        */
+                       printk(KERN_DEBUG
+                              "%s: Wrong control channel in "
+                              " association response: configured"
+                              " center-freq: %d hti-cfreq: %d "
+                              " hti->control_chan: %d band: %d. "
+                              " Disabling HT transmit.\n",
+                              sdata->name,
+                              local->hw.conf.channel->center_freq,
+                              hti_cfreq, hti->control_chan,
+                              sband->band);
+                       hti = &bss_hti;
+                       disable_tx_ht = true;
+               } else {
+                       printk(KERN_DEBUG
+                              "%s: Wrong control channel in "
+                              " association response: configured"
+                              " center-freq: %d hti-cfreq: %d "
+                              " hti->control_chan: %d band: %d. "
+                              " Disabling HT.\n",
+                              sdata->name,
+                              local->hw.conf.channel->center_freq,
+                              hti_cfreq, hti->control_chan,
+                              sband->band);
+                       enable_ht = false;
+               }
        }
 
        if (enable_ht) {
@@ -241,6 +282,8 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
        }
 
        tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
+       if (disable_tx_ht)
+               tx_channel_type = NL80211_CHAN_NO_HT;
 
        if (local->tmp_channel)
                local->tmp_channel_type = rx_channel_type;
@@ -2104,7 +2147,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
                                               cbss->bssid, ap_ht_cap_flags,
-                                              false);
+                                              false,
+                                              (struct ieee80211_bss *)
+                                              cbss->priv);
 
        /* set AID and assoc capability,
         * ieee80211_set_associated() will tell the driver */
@@ -2537,7 +2582,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                rcu_read_unlock();
 
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
-                                              bssid, ap_ht_cap_flags, true);
+                                              bssid, ap_ht_cap_flags, true,
+                                              NULL);
        }
 
        /* Note: country IE parsing is done for us by cfg80211 */
index c70e176..f097300 100644 (file)
@@ -173,6 +173,16 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                        bss->valid_data |= IEEE80211_BSS_VALID_WMM;
        }
 
+       if (elems->ht_info_elem != NULL &&
+           (!elems->parse_error ||
+            !(bss->valid_data & IEEE80211_BSS_VALID_HT))) {
+               memcpy(&bss->ht_info, elems->ht_info_elem,
+                      sizeof(bss->ht_info));
+               bss->has_ht_info = true;
+               if (!elems->parse_error)
+                       bss->valid_data |= IEEE80211_BSS_VALID_HT;
+       }
+
        if (!beacon)
                bss->last_probe_resp = jiffies;