diff -Naur 1/include/net/mac80211.h 2/include/net/mac80211.h --- 1/include/net/mac80211.h 2016-02-25 21:01:36.000000000 +0100 +++ 2/include/net/mac80211.h 2016-04-07 09:05:21.474203687 +0200 @@ -867,7 +867,16 @@ /* 2 bytes free */ }; /* only needed before rate control */ - unsigned long jiffies; + struct { + /* same position as rates[0] */ + struct ieee80211_tx_rate prerate; + u8 use_preset_rate:1; + u8 use_legacy_rate:1; + /* only used for legacy bitrates */ + u16 bitrate; + /* 2 bytes free */ + unsigned long jiffies; + }; }; /* NB: vif can be NULL for injected frames */ struct ieee80211_vif *vif; diff -Naur 1/net/mac80211/tx.c 2/net/mac80211/tx.c --- 1/net/mac80211/tx.c 2016-02-25 21:01:36.000000000 +0100 +++ 2/net/mac80211/tx.c 2016-04-07 09:05:21.478203760 +0200 @@ -786,6 +786,52 @@ } static ieee80211_tx_result debug_noinline +ieee80211_tx_h_rate_preset(struct ieee80211_tx_data *tx) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_supported_band *sband; + u16 bitrate; + u8 use_legacy_rate; + s8 idx; + int i; + + sband = tx->local->hw.wiphy->bands[info->band]; + + bitrate = info->control.bitrate; + use_legacy_rate = info->control.use_legacy_rate; + + /* + * WARNING: This overwrites some data that may be needed in the + * following procedure. So everything that is needed and shares memory + * with info->control.rates[] needs to be copied beforehand. + */ + for (i = 1; i < IEEE80211_TX_MAX_RATES; i++) + info->control.rates[i].idx = -1; + + if (!use_legacy_rate) { + if (info->control.rates[0].idx < 0) + return TX_DROP; + return TX_CONTINUE; + } + + idx = -1; + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate > bitrate) + continue; + if (idx >= 0 && + sband->bitrates[i].bitrate < sband->bitrates[idx].bitrate) + continue; + idx = i; + } + if (unlikely(idx < 0)) + return TX_DROP; + + info->control.rates[0].idx = idx; + + return TX_CONTINUE; +} + +static ieee80211_tx_result debug_noinline ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); @@ -1476,8 +1522,12 @@ CALL_TXH(ieee80211_tx_h_ps_buf); CALL_TXH(ieee80211_tx_h_check_control_port_protocol); CALL_TXH(ieee80211_tx_h_select_key); - if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL)) - CALL_TXH(ieee80211_tx_h_rate_ctrl); + if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL)) { + if (unlikely(info->control.use_preset_rate)) + CALL_TXH(ieee80211_tx_h_rate_preset); + else + CALL_TXH(ieee80211_tx_h_rate_ctrl); + } if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) { __skb_queue_tail(&tx->skbs, tx->skb); @@ -1674,9 +1724,14 @@ int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); u16 txflags; + u8 known, flags, mcs; info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_DONTFRAG; + info->control.prerate.count = 1; + info->control.prerate.flags = 0; + info->control.use_preset_rate = 0; + info->control.use_legacy_rate = 0; /* * for every radiotap entry that is present @@ -1722,6 +1777,46 @@ txflags = get_unaligned_le16(iterator.this_arg); if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) info->flags |= IEEE80211_TX_CTL_NO_ACK; + if (txflags & IEEE80211_RADIOTAP_F_TX_CTS) + info->control.prerate.flags |= + IEEE80211_TX_RC_USE_CTS_PROTECT; + if (txflags & IEEE80211_RADIOTAP_F_TX_RTS) + info->control.prerate.flags |= + IEEE80211_TX_RC_USE_RTS_CTS; + break; + + case IEEE80211_RADIOTAP_RATE: + info->control.bitrate = *iterator.this_arg * 5; + info->control.use_legacy_rate = 1; + info->control.use_preset_rate = 1; + break; + + case IEEE80211_RADIOTAP_MCS: + known = *iterator.this_arg; + flags = *(iterator.this_arg + 1); + mcs = *(iterator.this_arg + 2); + if (known & IEEE80211_RADIOTAP_MCS_HAVE_MCS) { + info->control.prerate.idx = mcs; + info->control.prerate.flags |= + IEEE80211_TX_RC_MCS; + info->control.use_preset_rate = 1; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_BW) { + if ((flags & IEEE80211_RADIOTAP_MCS_BW_MASK) + == IEEE80211_RADIOTAP_MCS_BW_40) + info->control.prerate.flags |= + IEEE80211_TX_RC_40_MHZ_WIDTH; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_GI) { + if (flags & IEEE80211_RADIOTAP_MCS_SGI) + info->control.prerate.flags |= + IEEE80211_TX_RC_SHORT_GI; + } + if (known & IEEE80211_RADIOTAP_MCS_HAVE_FMT) { + if (flags & IEEE80211_RADIOTAP_MCS_FMT_GF) + info->control.prerate.flags |= + IEEE80211_TX_RC_GREEN_FIELD; + } break; /*