Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0aa75f0 authored by Johannes Berg's avatar Johannes Berg
Browse files

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 34fb190e
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -1460,13 +1460,16 @@ struct ieee80211_ht_operation {
 *	STA can receive. Rate expressed in units of 1 Mbps.
 *	If this field is 0 this value should not be used to
 *	consider the highest RX data rate supported.
 *	The top 3 bits of this field are reserved.
 *	The top 3 bits of this field indicate the Maximum NSTS,total
 *	(a beamformee capability.)
 * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams
 * @tx_highest: Indicates highest long GI VHT PPDU data rate
 *	STA can transmit. Rate expressed in units of 1 Mbps.
 *	If this field is 0 this value should not be used to
 *	consider the highest TX data rate supported.
 *	The top 3 bits of this field are reserved.
 *	The top 2 bits of this field are reserved, the
 *	3rd bit from the top indiciates VHT Extended NSS BW
 *	Capability.
 */
struct ieee80211_vht_mcs_info {
	__le16 rx_mcs_map;
@@ -1475,6 +1478,13 @@ struct ieee80211_vht_mcs_info {
	__le16 tx_highest;
} __packed;

/* for rx_highest */
#define IEEE80211_VHT_MAX_NSTS_TOTAL_SHIFT	13
#define IEEE80211_VHT_MAX_NSTS_TOTAL_MASK	(7 << IEEE80211_VHT_MAX_NSTS_TOTAL_SHIFT)

/* for tx_highest */
#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE	(1 << 13)

/**
 * enum ieee80211_vht_mcs_support - VHT MCS support definitions
 * @IEEE80211_VHT_MCS_SUPPORT_0_7: MCSes 0-7 are supported for the
@@ -1650,6 +1660,7 @@ struct ieee80211_mu_edca_param_set {
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ		0x00000004
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ	0x00000008
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK			0x0000000C
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_SHIFT			2
#define IEEE80211_VHT_CAP_RXLDPC				0x00000010
#define IEEE80211_VHT_CAP_SHORT_GI_80				0x00000020
#define IEEE80211_VHT_CAP_SHORT_GI_160				0x00000040
@@ -1678,6 +1689,26 @@ struct ieee80211_mu_edca_param_set {
#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB	0x0c000000
#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN			0x10000000
#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN			0x20000000
#define IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT			30
#define IEEE80211_VHT_CAP_EXT_NSS_BW_MASK			0xc0000000

/**
 * ieee80211_get_vht_max_nss - return max NSS for a given bandwidth/MCS
 * @cap: VHT capabilities of the peer
 * @bw: bandwidth to use
 * @mcs: MCS index to use
 * @ext_nss_bw_capable: indicates whether or not the local transmitter
 *	(rate scaling algorithm) can deal with the new logic
 *	(dot11VHTExtendedNSSBWCapable)
 *
 * Due to the VHT Extended NSS Bandwidth Support, the maximum NSS can
 * vary for a given BW/MCS. This function parses the data.
 *
 * Note: This function is exported by cfg80211.
 */
int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
			      enum ieee80211_vht_chanwidth bw,
			      int mcs, bool ext_nss_bw_capable);

/* 802.11ax HE MAC capabilities */
#define IEEE80211_HE_MAC_CAP0_HTC_HE				0x01
+109 −0
Original line number Diff line number Diff line
@@ -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);