ieee80211: add new VHT capability fields/parsing

IEEE 802.11-2016 extended the VHT capability fields to allow
indicating the number of spatial streams depending on the
actually used bandwidth, add support for decoding this.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg
2018-08-31 11:31:16 +03:00
parent 34fb190ec0
commit b0aa75f0b1
2 changed files with 142 additions and 2 deletions

View File

@@ -5,17 +5,20 @@
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2017 Intel Deutschland GmbH
* Copyright (C) 2018 Intel Corporation
*/
#include <linux/export.h>
#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <net/ip.h>
#include <net/dsfield.h>
#include <linux/if_vlan.h>
#include <linux/mpls.h>
#include <linux/gcd.h>
#include <linux/bitfield.h>
#include "core.h"
#include "rdev-ops.h"
@@ -1938,3 +1941,109 @@ void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr)
netif_rx_ni(skb);
}
EXPORT_SYMBOL(cfg80211_send_layer2_update);
int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
enum ieee80211_vht_chanwidth bw,
int mcs, bool ext_nss_bw_capable)
{
u16 map = le16_to_cpu(cap->supp_mcs.rx_mcs_map);
int max_vht_nss = 0;
int ext_nss_bw;
int supp_width;
int i, mcs_encoding;
if (map == 0xffff)
return 0;
if (WARN_ON(mcs > 9))
return 0;
if (mcs <= 7)
mcs_encoding = 0;
else if (mcs == 8)
mcs_encoding = 1;
else
mcs_encoding = 2;
/* find max_vht_nss for the given MCS */
for (i = 7; i >= 0; i--) {
int supp = (map >> (2 * i)) & 3;
if (supp == 3)
continue;
if (supp >= mcs_encoding) {
max_vht_nss = i;
break;
}
}
if (!(cap->supp_mcs.tx_mcs_map &
cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE)))
return max_vht_nss;
ext_nss_bw = le32_get_bits(cap->vht_cap_info,
IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
supp_width = le32_get_bits(cap->vht_cap_info,
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
/* if not capable, treat ext_nss_bw as 0 */
if (!ext_nss_bw_capable)
ext_nss_bw = 0;
/* This is invalid */
if (supp_width == 3)
return 0;
/* This is an invalid combination so pretend nothing is supported */
if (supp_width == 2 && (ext_nss_bw == 1 || ext_nss_bw == 2))
return 0;
/*
* Cover all the special cases according to IEEE 802.11-2016
* Table 9-250. All other cases are either factor of 1 or not
* valid/supported.
*/
switch (bw) {
case IEEE80211_VHT_CHANWIDTH_USE_HT:
case IEEE80211_VHT_CHANWIDTH_80MHZ:
if ((supp_width == 1 || supp_width == 2) &&
ext_nss_bw == 3)
return 2 * max_vht_nss;
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
if (supp_width == 0 &&
(ext_nss_bw == 1 || ext_nss_bw == 2))
return DIV_ROUND_UP(max_vht_nss, 2);
if (supp_width == 0 &&
ext_nss_bw == 3)
return DIV_ROUND_UP(3 * max_vht_nss, 4);
if (supp_width == 1 &&
ext_nss_bw == 3)
return 2 * max_vht_nss;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
if (supp_width == 0 &&
(ext_nss_bw == 1 || ext_nss_bw == 2))
return 0; /* not possible */
if (supp_width == 0 &&
ext_nss_bw == 2)
return DIV_ROUND_UP(max_vht_nss, 2);
if (supp_width == 0 &&
ext_nss_bw == 3)
return DIV_ROUND_UP(3 * max_vht_nss, 4);
if (supp_width == 1 &&
ext_nss_bw == 0)
return 0; /* not possible */
if (supp_width == 1 &&
ext_nss_bw == 1)
return DIV_ROUND_UP(max_vht_nss, 2);
if (supp_width == 1 &&
ext_nss_bw == 2)
return DIV_ROUND_UP(3 * max_vht_nss, 4);
break;
}
/* not covered or invalid combination received */
return max_vht_nss;
}
EXPORT_SYMBOL(ieee80211_get_vht_max_nss);