nl80211: add VHT support for set_bitrate_mask
authorJanusz Dziedzic <janusz.dziedzic@tieto.com>
Thu, 5 Dec 2013 19:42:58 +0000 (20:42 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 16 Dec 2013 15:05:17 +0000 (16:05 +0100)
Add VHT MCS/NSS set support for nl80211_set_tx_bitrate_mask().
This should be used mainly for test purpose, to check
different MCS/NSS VHT combinations.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/nl80211.c

index 6c3a650f3a8fc08fb4adb7bc129d7a7f9f381a1b..80a10212b1b9bee63eafcae9d3142e6aee18d9e2 100644 (file)
@@ -1767,6 +1767,7 @@ struct cfg80211_bitrate_mask {
        struct {
                u32 legacy;
                u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
+               u16 vht_mcs[NL80211_VHT_NSS_MAX];
        } control[IEEE80211_NUM_BANDS];
 };
 /**
index 6e700645cd27d505fc80bbaaf2588a96e02addca..e1307909ecf118728434286aa2150da73d4d0225 100644 (file)
@@ -3112,6 +3112,8 @@ enum nl80211_key_attributes {
  *     %NL80211_MAX_SUPP_RATES in a single array).
  * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
  *     in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ *     see &struct nl80211_txrate_vht
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
@@ -3119,6 +3121,7 @@ enum nl80211_tx_rate_attributes {
        __NL80211_TXRATE_INVALID,
        NL80211_TXRATE_LEGACY,
        NL80211_TXRATE_HT,
+       NL80211_TXRATE_VHT,
 
        /* keep last */
        __NL80211_TXRATE_AFTER_LAST,
@@ -3126,6 +3129,15 @@ enum nl80211_tx_rate_attributes {
 };
 
 #define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX            8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+       __u16 mcs[NL80211_VHT_NSS_MAX];
+};
 
 /**
  * enum nl80211_band - Frequency band
index 2d0c19c6133b3586d2f512fcb487396a349bf091..04681a46eda8fc974997041e52b2709882755720 100644 (file)
@@ -7328,11 +7328,72 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
        return true;
 }
 
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+       u16 mcs_mask = 0;
+
+       switch (vht_mcs_map) {
+       case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_7:
+               mcs_mask = 0x00FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_8:
+               mcs_mask = 0x01FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_9:
+               mcs_mask = 0x03FF;
+               break;
+       default:
+               break;
+       }
+
+       return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+                              u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+       u8 nss;
+
+       for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+               vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+               vht_mcs_map >>= 2;
+       }
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+                            struct nl80211_txrate_vht *txrate,
+                            u16 mcs[NL80211_VHT_NSS_MAX])
+{
+       u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+       u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+       u8 i;
+
+       if (!sband->vht_cap.vht_supported)
+               return false;
+
+       memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+       /* Build vht_mcs_mask from VHT capabilities */
+       vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+                       mcs[i] = txrate->mcs[i];
+               else
+                       return false;
+       }
+
+       return true;
+}
+
 static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
        [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
                                    .len = NL80211_MAX_SUPP_RATES },
        [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
                                .len = NL80211_MAX_SUPP_HT_RATES },
+       [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7345,6 +7406,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
        struct net_device *dev = info->user_ptr[1];
        struct nlattr *tx_rates;
        struct ieee80211_supported_band *sband;
+       u16 vht_tx_mcs_map;
 
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
@@ -7361,6 +7423,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                memcpy(mask.control[i].ht_mcs,
                       sband->ht_cap.mcs.rx_mask,
                       sizeof(mask.control[i].ht_mcs));
+
+               if (!sband->vht_cap.vht_supported)
+                       continue;
+
+               vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+               vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
        }
 
        /* if no rates are given set it back to the defaults */
@@ -7399,20 +7467,32 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                        mask.control[band].ht_mcs))
                                return -EINVAL;
                }
+               if (tb[NL80211_TXRATE_VHT]) {
+                       if (!vht_set_mcs_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_VHT]),
+                                       mask.control[band].vht_mcs))
+                               return -EINVAL;
+               }
 
                if (mask.control[band].legacy == 0) {
-                       /* don't allow empty legacy rates if HT
-                        * is not even supported. */
-                       if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+                       /* don't allow empty legacy rates if HT or VHT
+                        * are not even supported.
+                        */
+                       if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+                             rdev->wiphy.bands[band]->vht_cap.vht_supported))
                                return -EINVAL;
 
                        for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
                                if (mask.control[band].ht_mcs[i])
-                                       break;
+                                       goto out;
+
+                       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                               if (mask.control[band].vht_mcs[i])
+                                       goto out;
 
                        /* legacy and mcs rates may not be both empty */
-                       if (i == IEEE80211_HT_MCS_MASK_LEN)
-                               return -EINVAL;
+                       return -EINVAL;
                }
        }