]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/rate.c
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
[karo-tx-linux.git] / net / mac80211 / rate.c
index ad64f4d5271a58121f93cf7f62806c93687d1193..b4f7600a3e36c010f6d428ad0461db2324087dc3 100644 (file)
@@ -159,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)
@@ -184,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
@@ -293,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;
 
@@ -303,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;
                }
        }
 
@@ -312,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;
                }
        }
@@ -335,6 +437,7 @@ 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 && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) {
                ista = &sta->sta;
@@ -344,7 +447,7 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                info->control.rates[i].idx = -1;
                info->control.rates[i].flags = 0;
-               info->control.rates[i].count = 1;
+               info->control.rates[i].count = 0;
        }
 
        if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
@@ -358,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
@@ -372,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();
 
@@ -417,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);
@@ -440,6 +529,6 @@ void rate_control_deinitialize(struct ieee80211_local *local)
                return;
 
        local->rate_ctrl = NULL;
-       rate_control_put(ref);
+       rate_control_free(ref);
 }