]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/ath9k/rc.c
ath9k: Fix PTK/GTK handshake timeout
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / ath9k / rc.c
index 8bc7bb50c7fc6aba8f21aad54aa7fa79180fa76d..0e3e2b7dd2ecb6ee3ba0ca79d651e4b1fbd35a23 100644 (file)
@@ -15,7 +15,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "core.h"
+#include "ath9k.h"
 
 static struct ath_rate_table ar5416_11na_ratetable = {
        42,
@@ -747,14 +747,17 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
        return rate;
 }
 
-static void ath_rc_rate_set_series(struct ath_rate_table *rate_table ,
+static void ath_rc_rate_set_series(struct ath_rate_table *rate_table,
                                   struct ieee80211_tx_rate *rate,
+                                  struct ieee80211_tx_rate_control *txrc,
                                   u8 tries, u8 rix, int rtsctsenable)
 {
        rate->count = tries;
        rate->idx = rix;
 
-       if (rtsctsenable)
+       if (txrc->short_preamble)
+               rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
+       if (txrc->rts || rtsctsenable)
                rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
        if (WLAN_RC_PHY_40(rate_table->info[rix].phy))
                rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
@@ -764,6 +767,43 @@ static void ath_rc_rate_set_series(struct ath_rate_table *rate_table ,
                rate->flags |= IEEE80211_TX_RC_MCS;
 }
 
+static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
+                                  struct ath_rate_table *rate_table,
+                                  struct ieee80211_tx_info *tx_info)
+{
+       struct ieee80211_tx_rate *rates = tx_info->control.rates;
+       int i = 0, rix = 0, cix, enable_g_protection = 0;
+
+       /* get the cix for the lowest valid rix */
+       for (i = 3; i >= 0; i--) {
+               if (rates[i].count && (rates[i].idx >= 0)) {
+                       rix = rates[i].idx;
+                       break;
+               }
+       }
+       cix = rate_table->info[rix].ctrl_rate;
+
+       /* All protection frames are transmited at 2Mb/s for 802.11g,
+        * otherwise we transmit them at 1Mb/s */
+       if (sc->hw->conf.channel->band == IEEE80211_BAND_2GHZ &&
+           !conf_is_ht(&sc->hw->conf))
+               enable_g_protection = 1;
+
+       /*
+        * If 802.11g protection is enabled, determine whether to use RTS/CTS or
+        * just CTS.  Note that this is only done for OFDM/HT unicast frames.
+        */
+       if ((sc->sc_flags & SC_OP_PROTECT_ENABLE) &&
+           !(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+           (rate_table->info[rix].phy == WLAN_RC_PHY_OFDM ||
+            WLAN_RC_PHY_HT(rate_table->info[rix].phy))) {
+               rates[0].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT;
+               cix = rate_table->info[enable_g_protection].ctrl_rate;
+       }
+
+       tx_info->control.rts_cts_rate_idx = cix;
+}
+
 static u8 ath_rc_rate_getidx(struct ath_softc *sc,
                             struct ath_rate_priv *ath_rc_priv,
                             struct ath_rate_table *rate_table,
@@ -801,6 +841,8 @@ static void ath_rc_ratefind(struct ath_softc *sc,
        struct sk_buff *skb = txrc->skb;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_tx_rate *rates = tx_info->control.rates;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       __le16 fc = hdr->frame_control;
        u8 try_per_rate = 0, i = 0, rix, nrix;
        int is_probe = 0;
 
@@ -811,7 +853,7 @@ static void ath_rc_ratefind(struct ath_softc *sc,
        if (is_probe) {
                /* set one try for probe rates. For the
                 * probes don't enable rts */
-               ath_rc_rate_set_series(rate_table, &rates[i++],
+               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
                                       1, nrix, 0);
 
                try_per_rate = (ATH_11N_TXMAXTRY/4);
@@ -820,12 +862,12 @@ static void ath_rc_ratefind(struct ath_softc *sc,
                 */
                nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
                                          rate_table, nrix, 1, 0);
-               ath_rc_rate_set_series(rate_table, &rates[i++],
+               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
                                       try_per_rate, nrix, 0);
        } else {
                try_per_rate = (ATH_11N_TXMAXTRY/4);
                /* Set the choosen rate. No RTS for first series entry. */
-               ath_rc_rate_set_series(rate_table, &rates[i++],
+               ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
                                       try_per_rate, nrix, 0);
        }
 
@@ -841,7 +883,7 @@ static void ath_rc_ratefind(struct ath_softc *sc,
                nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
                                          rate_table, nrix, 1, min_rate);
                /* All other rates in the series have RTS enabled */
-               ath_rc_rate_set_series(rate_table, &rates[i],
+               ath_rc_rate_set_series(rate_table, &rates[i], txrc,
                                       try_num, nrix, 1);
        }
 
@@ -871,6 +913,24 @@ static void ath_rc_ratefind(struct ath_softc *sc,
                        rates[3].flags = rates[2].flags;
                }
        }
+
+       /*
+        * Force hardware to use computed duration for next
+        * fragment by disabling multi-rate retry, which
+        * updates duration based on the multi-rate duration table.
+        *
+        * FIXME: Fix duration
+        */
+       if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+           (ieee80211_has_morefrags(fc) ||
+            (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG))) {
+               rates[1].count = rates[2].count = rates[3].count = 0;
+               rates[1].idx = rates[2].idx = rates[3].idx = 0;
+               rates[0].count = ATH_TXMAXTRY;
+       }
+
+       /* Setup RTS/CTS */
+       ath_rc_rate_set_rtscts(sc, rate_table, tx_info);
 }
 
 static bool ath_rc_update_per(struct ath_softc *sc,
@@ -1207,6 +1267,8 @@ static void ath_rc_update_ht(struct ath_softc *sc,
                ath_rc_priv->per_down_time = now_msec;
        }
 
+       ath_debug_stat_retries(sc, tx_rate, xretries, retries);
+
 #undef CHK_RSSI
 }
 
@@ -1330,15 +1392,16 @@ static void ath_rc_init(struct ath_softc *sc,
        struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
        u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates;
        u8 i, j, k, hi = 0, hthi = 0;
+       struct ath_hw *ah = sc->sc_ah;
 
        /* FIXME: Adhoc */
-       if ((sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) ||
-           (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC)) {
+       if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) ||
+           (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) {
                bool is_cw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
                rate_table = ath_choose_rate_table(sc, sband->band,
                                                   sta->ht_cap.ht_supported,
                                                   is_cw_40);
-       } else if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) {
+       } else if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
                /* cur_rate_table would be set on init through config() */
                rate_table = sc->cur_rate_table;
        }
@@ -1350,7 +1413,8 @@ static void ath_rc_init(struct ath_softc *sc,
 
        if (sta->ht_cap.ht_supported) {
                ath_rc_priv->ht_cap = WLAN_RC_HT_FLAG;
-               if (sc->sc_ah->ah_caps.tx_chainmask != 1)
+               if (sc->sc_ah->caps.tx_chainmask != 1 &&
+                       ath9k_hw_getcapability(ah, ATH9K_CAP_DS, 0, NULL))
                        ath_rc_priv->ht_cap |= WLAN_RC_DS_FLAG;
                if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
                        ath_rc_priv->ht_cap |= WLAN_RC_40_FLAG;
@@ -1385,16 +1449,16 @@ static void ath_rc_init(struct ath_softc *sc,
        if (!rateset->rs_nrates) {
                /* No working rate, just initialize valid rates */
                hi = ath_rc_init_validrates(ath_rc_priv, rate_table,
-                                               ath_rc_priv->ht_cap);
+                                           ath_rc_priv->ht_cap);
        } else {
                /* Use intersection of working rates and valid rates */
                hi = ath_rc_setvalid_rates(ath_rc_priv, rate_table,
-                                              rateset, ath_rc_priv->ht_cap);
+                                          rateset, ath_rc_priv->ht_cap);
                if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) {
                        hthi = ath_rc_setvalid_htrates(ath_rc_priv,
-                                                          rate_table,
-                                                          ht_mcs,
-                                                          ath_rc_priv->ht_cap);
+                                                      rate_table,
+                                                      ht_mcs,
+                                                      ath_rc_priv->ht_cap);
                }
                hi = A_MAX(hi, hthi);
        }
@@ -1457,7 +1521,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
         */
        if (tx_info_priv->tx.ts_flags &
            (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
-           ((sc->sc_ah->ah_txTrigLevel) >= ath_rc_priv->tx_triglevel_max)) {
+           ((sc->sc_ah->tx_trig_level) >= ath_rc_priv->tx_triglevel_max)) {
                tx_status = 1;
                is_underrun = 1;
        }
@@ -1471,7 +1535,8 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
                         tx_info_priv->tx.ts_longretry);
 
        /* Check if aggregation has to be enabled for this tid */
-       if (conf_is_ht(&sc->hw->conf)) {
+       if (conf_is_ht(&sc->hw->conf) &&
+           !(skb->protocol == cpu_to_be16(ETH_P_PAE))) {
                if (ieee80211_is_data_qos(fc)) {
                        u8 *qc, tid;
                        struct ath_node *an;
@@ -1484,6 +1549,8 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
                                ieee80211_start_tx_ba_session(sc->hw, hdr->addr1, tid);
                }
        }
+
+       ath_debug_stat_rc(sc, skb);
 exit:
        kfree(tx_info_priv);
 }
@@ -1564,7 +1631,7 @@ static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp
        }
 
        rate_priv->rssi_down_time = jiffies_to_msecs(jiffies);
-       rate_priv->tx_triglevel_max = sc->sc_ah->ah_caps.tx_triglevel_max;
+       rate_priv->tx_triglevel_max = sc->sc_ah->caps.tx_triglevel_max;
 
        return rate_priv;
 }