aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/rate.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/rate.c')
-rw-r--r--net/mac80211/rate.c193
1 files changed, 156 insertions, 37 deletions
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 816590b..3313c11 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "rate.h"
#include "ieee80211_i.h"
#include "debugfs.h"
@@ -144,7 +145,7 @@ static ssize_t rcname_read(struct file *file, char __user *userbuf,
static const struct file_operations rcname_ops = {
.read = rcname_read,
- .open = mac80211_open_file_generic,
+ .open = simple_open,
.llseek = default_llseek,
};
#endif
@@ -158,7 +159,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
if (!ref)
goto fail_ref;
- kref_init(&ref->kref);
ref->local = local;
ref->ops = ieee80211_rate_control_ops_get(name);
if (!ref->ops)
@@ -183,11 +183,8 @@ fail_ref:
return NULL;
}
-static void rate_control_release(struct kref *kref)
+static void rate_control_free(struct rate_control_ref *ctrl_ref)
{
- struct rate_control_ref *ctrl_ref;
-
- ctrl_ref = container_of(kref, struct rate_control_ref, kref);
ctrl_ref->ops->free(ctrl_ref->priv);
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -199,7 +196,7 @@ static void rate_control_release(struct kref *kref)
kfree(ctrl_ref);
}
-static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc)
+static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
{
struct sk_buff *skb = txrc->skb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -208,7 +205,9 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc)
fc = hdr->frame_control;
- return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc);
+ return (info->flags & (IEEE80211_TX_CTL_NO_ACK |
+ IEEE80211_TX_CTL_USE_MINRATE)) ||
+ !ieee80211_is_data(fc);
}
static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
@@ -233,6 +232,27 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
/* could not find a basic rate; use original selection */
}
+static inline s8
+rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta)
+{
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ struct ieee80211_rate *srate = &sband->bitrates[i];
+ if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
+ (srate->bitrate == 55) || (srate->bitrate == 110))
+ continue;
+
+ if (rate_supported(sta, sband->band, i))
+ return i;
+ }
+
+ /* No matching rate found */
+ return 0;
+}
+
+
bool rate_control_send_low(struct ieee80211_sta *sta,
void *priv_sta,
struct ieee80211_tx_rate_control *txrc)
@@ -241,8 +261,14 @@ bool rate_control_send_low(struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband = txrc->sband;
int mcast_rate;
- if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) {
- info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta);
+ if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
+ if ((sband->band != IEEE80211_BAND_2GHZ) ||
+ !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+ info->control.rates[0].idx =
+ rate_lowest_index(txrc->sband, sta);
+ else
+ info->control.rates[0].idx =
+ rate_lowest_non_cck_index(txrc->sband, sta);
info->control.rates[0].count =
(info->flags & IEEE80211_TX_CTL_NO_ACK) ?
1 : txrc->hw->max_rate_tries;
@@ -263,8 +289,8 @@ bool rate_control_send_low(struct ieee80211_sta *sta,
}
EXPORT_SYMBOL(rate_control_send_low);
-static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
- int n_bitrates, u32 mask)
+static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate,
+ int n_bitrates, u32 mask)
{
int j;
@@ -273,7 +299,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
- return;
+ return true;
}
}
@@ -282,6 +308,112 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+{
+ int i, j;
+ int ridx, rbit;
+
+ ridx = rate->idx / 8;
+ rbit = rate->idx % 8;
+
+ /* sanity check */
+ if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
+ return false;
+
+ /* See whether the selected rate or anything below it is allowed. */
+ for (i = ridx; i >= 0; i--) {
+ for (j = rbit; j >= 0; j--)
+ if (mcs_mask[i] & BIT(j)) {
+ rate->idx = i * 8 + j;
+ return true;
+ }
+ rbit = 7;
+ }
+
+ /* Try to find a higher rate that would be allowed */
+ ridx = (rate->idx + 1) / 8;
+ rbit = (rate->idx + 1) % 8;
+
+ for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ for (j = rbit; j < 8; j++)
+ if (mcs_mask[i] & BIT(j)) {
+ rate->idx = i * 8 + j;
+ return true;
+ }
+ rbit = 0;
+ }
+ return false;
+}
+
+
+
+static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
+ struct ieee80211_tx_rate_control *txrc,
+ u32 mask,
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
+{
+ struct ieee80211_tx_rate alt_rate;
+
+ /* handle HT rates */
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
+ if (rate_idx_match_mcs_mask(rate, mcs_mask))
+ return;
+
+ /* also try the legacy rates. */
+ alt_rate.idx = 0;
+ /* keep protection flags */
+ alt_rate.flags = rate->flags &
+ (IEEE80211_TX_RC_USE_RTS_CTS |
+ IEEE80211_TX_RC_USE_CTS_PROTECT |
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+ alt_rate.count = rate->count;
+ if (rate_idx_match_legacy_mask(&alt_rate,
+ txrc->sband->n_bitrates,
+ mask)) {
+ *rate = alt_rate;
+ return;
+ }
+ } else {
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ __le16 fc;
+
+ /* handle legacy rates */
+ if (rate_idx_match_legacy_mask(rate, txrc->sband->n_bitrates,
+ mask))
+ return;
+
+ /* if HT BSS, and we handle a data frame, also try HT rates */
+ if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT)
+ return;
+
+ fc = hdr->frame_control;
+ if (!ieee80211_is_data(fc))
+ return;
+
+ alt_rate.idx = 0;
+ /* keep protection flags */
+ alt_rate.flags = rate->flags &
+ (IEEE80211_TX_RC_USE_RTS_CTS |
+ IEEE80211_TX_RC_USE_CTS_PROTECT |
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+ alt_rate.count = rate->count;
+
+ alt_rate.flags |= IEEE80211_TX_RC_MCS;
+
+ if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) ||
+ (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS))
+ alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+ if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
+ *rate = alt_rate;
return;
}
}
@@ -305,8 +437,9 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
int i;
u32 mask;
+ u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
- if (sta) {
+ if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) {
ista = &sta->sta;
priv_sta = sta->rate_ctrl_priv;
}
@@ -328,10 +461,14 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
* the common case.
*/
mask = sdata->rc_rateidx_mask[info->band];
+ memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band],
+ sizeof(mcs_mask));
if (mask != (1 << txrc->sband->n_bitrates) - 1) {
if (sta) {
/* Filter out rates that the STA does not support */
mask &= sta->sta.supp_rates[info->band];
+ for (i = 0; i < sizeof(mcs_mask); i++)
+ mcs_mask[i] &= sta->sta.ht_cap.mcs.rx_mask[i];
}
/*
* Make sure the rate index selected for each TX rate is
@@ -342,32 +479,18 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
/* Skip invalid rates */
if (info->control.rates[i].idx < 0)
break;
- /* Rate masking supports only legacy rates for now */
- if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS)
- continue;
- rate_idx_match_mask(&info->control.rates[i],
- txrc->sband->n_bitrates, mask);
+ rate_idx_match_mask(&info->control.rates[i], txrc,
+ mask, mcs_mask);
}
}
BUG_ON(info->control.rates[0].idx < 0);
}
-struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
-{
- kref_get(&ref->kref);
- return ref;
-}
-
-void rate_control_put(struct rate_control_ref *ref)
-{
- kref_put(&ref->kref, rate_control_release);
-}
-
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
const char *name)
{
- struct rate_control_ref *ref, *old;
+ struct rate_control_ref *ref;
ASSERT_RTNL();
@@ -387,12 +510,8 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
return -ENOENT;
}
- old = local->rate_ctrl;
+ WARN_ON(local->rate_ctrl);
local->rate_ctrl = ref;
- if (old) {
- rate_control_put(old);
- sta_info_flush(local, NULL);
- }
wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n",
ref->ops->name);
@@ -410,6 +529,6 @@ void rate_control_deinitialize(struct ieee80211_local *local)
return;
local->rate_ctrl = NULL;
- rate_control_put(ref);
+ rate_control_free(ref);
}