]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/rc80211_minstrel_ht.c
Merge tag 'stable/for-linus-3.9-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[karo-tx-linux.git] / net / mac80211 / rc80211_minstrel_ht.c
index 9f9c453bc45d770ec18cc2746719df7ffa1f6859..3af141c6971214e9b1bb29b2976e858d86585da6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
        }                                                               \
 }
 
+#define CCK_DURATION(_bitrate, _short, _len)           \
+       (10 /* SIFS */ +                                \
+        (_short ? 72 + 24 : 144 + 48 ) +               \
+        (8 * (_len + 4) * 10) / (_bitrate))
+
+#define CCK_ACK_DURATION(_bitrate, _short)                     \
+       (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) +   \
+        CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE))
+
+#define CCK_DURATION_LIST(_short)                      \
+       CCK_ACK_DURATION(10, _short),                   \
+       CCK_ACK_DURATION(20, _short),                   \
+       CCK_ACK_DURATION(55, _short),                   \
+       CCK_ACK_DURATION(110, _short)
+
+#define CCK_GROUP                                              \
+       [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = {     \
+               .streams = 0,                                   \
+               .duration = {                                   \
+                       CCK_DURATION_LIST(false),               \
+                       CCK_DURATION_LIST(true)                 \
+               }                                               \
+       }
+
 /*
  * To enable sufficiently targeted rate sampling, MCS rates are divided into
  * groups, based on the number of streams and flags (HT40, SGI) that they
@@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_groups[] = {
 #if MINSTREL_MAX_STREAMS >= 3
        MCS_GROUP(3, 1, 1),
 #endif
+
+       /* must be last */
+       CCK_GROUP
 };
 
+#define MINSTREL_CCK_GROUP     (ARRAY_SIZE(minstrel_mcs_groups) - 1)
+
 static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
 
 /*
@@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
                         !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
 }
 
+static struct minstrel_rate_stats *
+minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+                     struct ieee80211_tx_rate *rate)
+{
+       int group, idx;
+
+       if (rate->flags & IEEE80211_TX_RC_MCS) {
+               group = minstrel_ht_get_group_idx(rate);
+               idx = rate->idx % MCS_GROUP_RATES;
+       } else {
+               group = MINSTREL_CCK_GROUP;
+
+               for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++)
+                       if (rate->idx == mp->cck_rates[idx])
+                               break;
+
+               /* short preamble */
+               if (!(mi->groups[group].supported & BIT(idx)))
+                       idx += 4;
+       }
+       return &mi->groups[group].rates[idx];
+}
+
 static inline struct minstrel_rate_stats *
 minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
 {
@@ -159,7 +211,7 @@ static void
 minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
 {
        struct minstrel_rate_stats *mr;
-       unsigned int usecs;
+       unsigned int usecs = 0;
 
        mr = &mi->groups[group].rates[rate];
 
@@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
                return;
        }
 
-       usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+       if (group != MINSTREL_CCK_GROUP)
+               usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+
        usecs += minstrel_mcs_groups[group].duration[rate];
        mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability);
 }
@@ -231,10 +285,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
                        if (!mr->cur_tp)
                                continue;
 
-                       /* ignore the lowest rate of each single-stream group */
-                       if (!i && minstrel_mcs_groups[group].streams == 1)
-                               continue;
-
                        if ((mr->cur_tp > cur_prob_tp && mr->probability >
                             MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) {
                                mg->max_prob_rate = index;
@@ -297,7 +347,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 }
 
 static bool
-minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
+minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate)
 {
        if (rate->idx < 0)
                return false;
@@ -305,7 +355,13 @@ minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
        if (!rate->count)
                return false;
 
-       return !!(rate->flags & IEEE80211_TX_RC_MCS);
+       if (rate->flags & IEEE80211_TX_RC_MCS)
+               return true;
+
+       return rate->idx == mp->cck_rates[0] ||
+              rate->idx == mp->cck_rates[1] ||
+              rate->idx == mp->cck_rates[2] ||
+              rate->idx == mp->cck_rates[3];
 }
 
 static void
@@ -390,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        struct minstrel_rate_stats *rate, *rate2;
        struct minstrel_priv *mp = priv;
        bool last;
-       int group;
        int i;
 
        if (!msp->is_ht)
@@ -419,13 +474,12 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
                mi->sample_packets += info->status.ampdu_len;
 
-       last = !minstrel_ht_txstat_valid(&ar[0]);
+       last = !minstrel_ht_txstat_valid(mp, &ar[0]);
        for (i = 0; !last; i++) {
                last = (i == IEEE80211_TX_MAX_RATES - 1) ||
-                      !minstrel_ht_txstat_valid(&ar[i + 1]);
+                      !minstrel_ht_txstat_valid(mp, &ar[i + 1]);
 
-               group = minstrel_ht_get_group_idx(&ar[i]);
-               rate = &mi->groups[group].rates[ar[i].idx % 8];
+               rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
 
                if (last)
                        rate->success += info->status.ampdu_ack_len;
@@ -451,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
 
        if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
                minstrel_ht_update_stats(mp, mi);
-               if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
+               if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
+                   mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
                        minstrel_aggr_check(sta, skb);
        }
 }
@@ -467,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        unsigned int ctime = 0;
        unsigned int t_slot = 9; /* FIXME */
        unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
+       unsigned int overhead = 0, overhead_rtscts = 0;
 
        mr = minstrel_get_ratestats(mi, index);
        if (mr->probability < MINSTREL_FRAC(1, 10)) {
@@ -488,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        ctime += (t_slot * cw) >> 1;
        cw = min((cw << 1) | 1, mp->cw_max);
 
+       if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) {
+               overhead = mi->overhead;
+               overhead_rtscts = mi->overhead_rtscts;
+       }
+
        /* Total TX time for data and Contention after first 2 tries */
-       tx_time = ctime + 2 * (mi->overhead + tx_time_data);
-       tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data);
+       tx_time = ctime + 2 * (overhead + tx_time_data);
+       tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data);
 
        /* See how many more tries we can fit inside segment size */
        do {
@@ -499,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                cw = min((cw << 1) | 1, mp->cw_max);
 
                /* Total TX time after this try */
-               tx_time += ctime + mi->overhead + tx_time_data;
-               tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data;
+               tx_time += ctime + overhead + tx_time_data;
+               tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
 
                if (tx_time_rtscts < mp->segment_size)
                        mr->retry_count_rtscts++;
@@ -530,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        else
                rate->count = mr->retry_count;
 
-       rate->flags = IEEE80211_TX_RC_MCS | group->flags;
+       rate->flags = 0;
        if (rtscts)
                rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
+
+       if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
+               rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
+               return;
+       }
+
+       rate->flags |= IEEE80211_TX_RC_MCS | group->flags;
        rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES;
 }
 
@@ -595,6 +663,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        return sample_idx;
 }
 
+static void
+minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp,
+                                   struct minstrel_ht_sta *mi, bool val)
+{
+       u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported;
+
+       if (!supported || !mi->cck_supported_short)
+               return;
+
+       if (supported & (mi->cck_supported_short << (val * 4)))
+               return;
+
+       supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4);
+       mi->groups[MINSTREL_CCK_GROUP].supported = supported;
+}
+
 static void
 minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                      struct ieee80211_tx_rate_control *txrc)
@@ -614,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
 
        info->flags |= mi->tx_flags;
+       minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
 
        /* Don't use EAPOL frames for sampling on non-mrr hw */
        if (mp->hw->max_rates == 1 &&
@@ -686,6 +771,30 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        }
 }
 
+static void
+minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+                      struct ieee80211_supported_band *sband,
+                      struct ieee80211_sta *sta)
+{
+       int i;
+
+       if (sband->band != IEEE80211_BAND_2GHZ)
+               return;
+
+       mi->cck_supported = 0;
+       mi->cck_supported_short = 0;
+       for (i = 0; i < 4; i++) {
+               if (!rate_supported(sta, sband->band, mp->cck_rates[i]))
+                       continue;
+
+               mi->cck_supported |= BIT(i);
+               if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
+                       mi->cck_supported_short |= BIT(i);
+       }
+
+       mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported;
+}
+
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                         struct ieee80211_sta *sta, void *priv_sta)
@@ -699,14 +808,13 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        int ack_dur;
        int stbc;
        int i;
-       unsigned int smps;
 
        /* fall back to the old minstrel for legacy stations */
        if (!sta->ht_cap.ht_supported)
                goto use_legacy;
 
        BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
-               MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS);
+               MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
 
        msp->is_ht = true;
        memset(mi, 0, sizeof(*mi));
@@ -735,28 +843,29 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
                mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
 
-       smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >>
-               IEEE80211_HT_CAP_SM_PS_SHIFT;
-
        for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
-               u16 req = 0;
-
                mi->groups[i].supported = 0;
-               if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
-                       if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
-                               req |= IEEE80211_HT_CAP_SGI_40;
-                       else
-                               req |= IEEE80211_HT_CAP_SGI_20;
+               if (i == MINSTREL_CCK_GROUP) {
+                       minstrel_ht_update_cck(mp, mi, sband, sta);
+                       continue;
                }
 
-               if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
-                       req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+               if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
+                       if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                               if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
+                                       continue;
+                       } else {
+                               if (!(sta_cap & IEEE80211_HT_CAP_SGI_20))
+                                       continue;
+                       }
+               }
 
-               if ((sta_cap & req) != req)
+               if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
+                   sta->bandwidth < IEEE80211_STA_RX_BW_40)
                        continue;
 
                /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
-               if (smps == WLAN_HT_CAP_SM_PS_STATIC &&
+               if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
                    minstrel_mcs_groups[i].streams > 1)
                        continue;