--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -18,6 +18,9 @@
 
 #define AVG_PKT_SIZE	1200
 
+/* Spatial stream error threshold in [%] */
+#define STREAM_ERROR_THRES 50
+
 /* Number of bits for an average sized packet */
 #define MCS_NBITS (AVG_PKT_SIZE << 3)
 
@@ -88,33 +91,26 @@
 /*
  * To enable sufficiently targeted rate sampling, MCS rates are divided into
  * groups, based on the number of streams and flags (HT40, SGI) that they
- * use.
+ * use. The groups are sorted by robustnes in order to enable stepwise
+ * increase in robustness by downgrading group index per stream to find
+ * best mix between spatial multiplexing and diversity.
  *
  * Sortorder has to be fixed for GROUP_IDX macro to be applicable:
  * HT40 -> SGI -> #streams
  */
 const struct mcs_group minstrel_mcs_groups[] = {
 	MCS_GROUP(1, 0, 0),
-	MCS_GROUP(2, 0, 0),
-#if MINSTREL_MAX_STREAMS >= 3
-	MCS_GROUP(3, 0, 0),
-#endif
-
 	MCS_GROUP(1, 1, 0),
-	MCS_GROUP(2, 1, 0),
-#if MINSTREL_MAX_STREAMS >= 3
-	MCS_GROUP(3, 1, 0),
-#endif
-
 	MCS_GROUP(1, 0, 1),
-	MCS_GROUP(2, 0, 1),
-#if MINSTREL_MAX_STREAMS >= 3
-	MCS_GROUP(3, 0, 1),
-#endif
-
 	MCS_GROUP(1, 1, 1),
+	MCS_GROUP(2, 0, 0),
+	MCS_GROUP(2, 1, 0),
+	MCS_GROUP(2, 0, 1),
 	MCS_GROUP(2, 1, 1),
 #if MINSTREL_MAX_STREAMS >= 3
+	MCS_GROUP(3, 0, 0),
+	MCS_GROUP(3, 1, 0),
+	MCS_GROUP(3, 0, 1),
 	MCS_GROUP(3, 1, 1),
 #endif
 
@@ -386,8 +382,8 @@ minstrel_ht_update_stats(struct minstrel
 	struct minstrel_mcs_group_data *mg;
 	struct minstrel_rate_stats *mr;
 	int group, i, j;
-	u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
-	u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
+	u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_cck_tp_rate[MAX_THR_RATES];
+	u8 index;
 
 	if (mi->ampdu_packets > 0) {
 		mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
@@ -414,10 +410,6 @@ minstrel_ht_update_stats(struct minstrel
 
 		mi->sample_count++;
 
-		/* (re)Initialize group rate indexes */
-		for(j = 0; j < MAX_THR_RATES; j++)
-			tmp_group_tp_rate[j] = group;
-
 		for (i = 0; i < MCS_GROUP_RATES; i++) {
 			if (!(mg->supported & BIT(i)))
 				continue;
@@ -441,16 +433,9 @@ minstrel_ht_update_stats(struct minstrel
 							       tmp_cck_tp_rate);
 			}
 
-			/* Find max throughput rate set within a group */
-			minstrel_ht_sort_best_tp_rates(mi, index,
-						       tmp_group_tp_rate);
-
 			/* Find max probability rate per group and global */
 			minstrel_ht_set_best_prob_rate(mi, index);
 		}
-
-		memcpy(mg->max_group_tp_rate, tmp_group_tp_rate,
-		       sizeof(mg->max_group_tp_rate));
 	}
 
 	/* Assign new rate set per sta */
@@ -516,27 +501,72 @@ minstrel_next_sample_idx(struct minstrel
 	}
 }
 
+/*
+ * Downgrading is done by lowering group index as group index is sorted
+ * in ascending order of robustness. When possible it first switches
+ * from SGI to LGI, second lowers HT40 to HT20 and then reduces the
+ * number of streams.
+ */
 static void
-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
+minstrel_ht_downgrade_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
-	int group, orig_group;
+	int i, cur_group, cur_idx, cur_flags, cur_streams;
+	int tmp_group, tmp_flags, tmp_streams;
 
-	orig_group = group = *idx / MCS_GROUP_RATES;
-	while (group > 0) {
-		group--;
+#ifdef CPTCFG_MAC80211_DEBUGFS
+      if (mp->fixed_rate_idx != -1)
+	                return;
+#endif
 
-		if (!mi->groups[group].supported)
+	for (i = 0; i < MAX_THR_RATES; i++) {
+		tmp_group = cur_group = mi->max_tp_rate[i] / MCS_GROUP_RATES;
+		cur_idx = mi->max_tp_rate[i] % MCS_GROUP_RATES;
+		cur_flags = minstrel_mcs_groups[cur_group].flags;
+		cur_streams = minstrel_mcs_groups[cur_group].streams;
+
+		/* Ignore CCK group and lowest single stream rate */
+		if ((cur_group == MINSTREL_CCK_GROUP) ||
+		    (cur_idx == 0 && cur_streams == 1)) {
 			continue;
+		}
 
-		if (minstrel_mcs_groups[group].streams >
-		    minstrel_mcs_groups[orig_group].streams)
-			continue;
+		while (tmp_group > 0) {
+			tmp_group--;
 
-		if (primary)
-			*idx = mi->groups[group].max_group_tp_rate[0];
-		else
-			*idx = mi->groups[group].max_group_tp_rate[1];
-		break;
+			tmp_flags = minstrel_mcs_groups[tmp_group].flags;
+			tmp_streams = minstrel_mcs_groups[tmp_group].streams;
+
+			if (!mi->groups[tmp_group].supported)
+				continue;
+
+			/* if possible switch SGI to LGI */
+			if ((cur_flags & IEEE80211_TX_RC_SHORT_GI) &&
+			    !(tmp_flags & IEEE80211_TX_RC_SHORT_GI) &&
+			    ((cur_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) == (tmp_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) &&
+			    (cur_streams == tmp_streams)) {
+				printk(KERN_ERR "Downgrade rate tp_max_rate[%i] from SGI rate %i -> to LGI rate %i\n", i, mi->max_tp_rate[i], tmp_group * MCS_GROUP_RATES + cur_idx);
+				mi->max_tp_rate[i] = tmp_group * MCS_GROUP_RATES + cur_idx;
+				break;
+			}
+
+			/* if possible switch HT40 to HT20 */
+			if ((cur_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+			    !(tmp_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+			    (cur_streams == tmp_streams)) {
+				printk(KERN_ERR "Downgrade rate tp_max_rate[%i] from HT40 rate %i -> to HT20 rate %i\n", i, mi->max_tp_rate[i], tmp_group * MCS_GROUP_RATES + cur_idx);
+				mi->max_tp_rate[i] = tmp_group * MCS_GROUP_RATES + cur_idx;
+				break;
+			}
+
+			/* if possible reduce streams by 1 */
+			if ((cur_streams > 1) &&
+			   !(tmp_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+			   !(tmp_flags & IEEE80211_TX_RC_SHORT_GI)) {
+				printk(KERN_ERR "Downgrade rate tp_max_rate[%i] by STREAMS %i from index %i to new index %i \n", i, cur_streams, mi->max_tp_rate[i], tmp_group * MCS_GROUP_RATES + cur_idx);
+				mi->max_tp_rate[i] = tmp_group * MCS_GROUP_RATES + cur_idx;
+				break;
+			}
+		}
 	}
 }
 
@@ -572,7 +602,7 @@ minstrel_ht_tx_status(void *priv, struct
 	struct minstrel_ht_sta *mi = &msp->ht;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_tx_rate *ar = info->status.rates;
-	struct minstrel_rate_stats *rate, *rate2;
+	struct minstrel_rate_stats *rate;
 	struct minstrel_priv *mp = priv;
 	bool last, update = false;
 	int i;
@@ -617,25 +647,35 @@ minstrel_ht_tx_status(void *priv, struct
 	}
 
 	/*
-	 * check for sudden death of spatial multiplexing,
-	 * downgrade to a lower number of streams if necessary.
+	 * Check for sudden death of spatial multiplexing by counting
+	 * spatial stream errors. If the ratio of block ack length
+	 * to AMPDU length is smaller than STREAM_ERROR_THRES in % a stream
+	 * error occurred. If the number of consecutive stream errors is
+	 * greater than 3 downgrade rate to increase robustness.
 	 */
-	rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
-	if (rate->attempts > 30 &&
-	    MINSTREL_FRAC(rate->success, rate->attempts) <
-	    MINSTREL_FRAC(20, 100)) {
-		minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
-		update = true;
-	}
-
-	rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
-	if (rate2->attempts > 30 &&
-	    MINSTREL_FRAC(rate2->success, rate2->attempts) <
-	    MINSTREL_FRAC(20, 100)) {
-		minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
-		update = true;
-	}
-
+	if ((info->status.ampdu_len > 1) &&
+	    (MINSTREL_FRAC(info->status.ampdu_ack_len, info->status.ampdu_len) <
+	    MINSTREL_FRAC(STREAM_ERROR_THRES, 100))) {
+		mi->stream_errors++;
+		if (mi->stream_errors > 3) {
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+			if (mp->simlple_downgrade != 1) {
+				minstrel_ht_downgrade_rate(mo, mi);
+			} else {
+				/* simplified downgrade version */
+				mi->tp_max_rate[0] = mi->mac_tp_rate[2];
+				mi->tp_max_rate[1] = mi->mac_tp_rate[3];
+			}
+#else
+			minstrel_ht_downgrade_rate(mp, mi);
+#endif
+			update = true;
+			/* reset stream error counter*/
+			mi->stream_errors = 0;
+		}
+	} else
+		mi->stream_errors = 0;
 	if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
 		update = true;
 		minstrel_ht_update_stats(mp, mi);
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -33,8 +33,7 @@ struct minstrel_mcs_group_data {
 	/* bitfield of supported MCS rates of this group */
 	u8 supported;
 
-	/* sorted rate set within a MCS group*/
-	u8 max_group_tp_rate[MAX_THR_RATES];
+	/* max probability rate within a MCS group */
 	u8 max_group_prob_rate;
 
 	/* MCS rate statistics */
@@ -67,6 +66,7 @@ struct minstrel_ht_sta {
 
 	/* tx flags to add for frames for this sta */
 	u32 tx_flags;
+	u8 stream_errors;
 
 	u8 sample_wait;
 	u8 sample_tries;
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -645,6 +645,9 @@ minstrel_alloc(struct ieee80211_hw *hw,
 	mp->fixed_rate_idx = (u32) -1;
 	mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx",
 			S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx);
+	mp->simple_downgrade = -1;
+	mp->dbg_downgrade = debugfs_create_u8("simple_rate_downgrade",
+			S_IRUGO | S_IWUGO, debugfsdir, &mp->simple_downgrade);
 #endif
 
 	minstrel_init_cck_rates(mp);
@@ -657,6 +660,7 @@ minstrel_free(void *priv)
 {
 #ifdef CPTCFG_MAC80211_DEBUGFS
 	debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate);
+	debugfs_remove(((struct minstrel_priv *)priv)->dbg_downgrade);
 #endif
 	kfree(priv);
 }
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -119,6 +119,10 @@ struct minstrel_priv {
 	 */
 	u32 fixed_rate_idx;
 	struct dentry *dbg_fixed_rate;
+
+	/* enable different methods to downgrade rates */
+	u8 simple_downgrade;
+	struct dentry *dbg_downgrade;
 #endif
 
 };
