|
@@ -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
|
|
@@ -63,6 +63,30 @@
|
|
|
} \
|
|
|
}
|
|
|
|
|
|
+#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);
|
|
|
}
|
|
@@ -293,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;
|
|
@@ -301,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
|
|
@@ -386,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)
|
|
@@ -415,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;
|
|
@@ -447,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);
|
|
|
}
|
|
|
}
|
|
@@ -463,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)) {
|
|
@@ -484,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 {
|
|
@@ -495,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++;
|
|
@@ -526,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;
|
|
|
}
|
|
|
|
|
@@ -591,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)
|
|
@@ -610,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 &&
|
|
@@ -682,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)
|
|
@@ -702,7 +815,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
|
|
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));
|
|
@@ -738,6 +851,11 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
|
|
u16 req = 0;
|
|
|
|
|
|
mi->groups[i].supported = 0;
|
|
|
+ if (i == MINSTREL_CCK_GROUP) {
|
|
|
+ minstrel_ht_update_cck(mp, mi, sband, sta);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
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;
|