diff mbox

[RFC,v2,3/7] ieee80211: add new VHT capability fields/parsing

Message ID 20170214132208.8715-4-johannes@sipsolutions.net
State RFC
Headers show

Commit Message

Johannes Berg Feb. 14, 2017, 1:22 p.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

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>
---
 include/linux/ieee80211.h | 105 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 103 insertions(+), 2 deletions(-)

Comments

Arend Van Spriel Feb. 15, 2017, 9:08 a.m. UTC | #1
On 14-2-2017 14:22, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
> 
> 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>
> ---
>  include/linux/ieee80211.h | 105 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 103 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 4a7200c6c9ea..c51d309a45e7 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -1457,13 +1457,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;
> @@ -1472,6 +1475,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
> @@ -1547,6 +1557,7 @@ struct ieee80211_vht_operation {
>  #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
> @@ -1575,6 +1586,96 @@ struct ieee80211_vht_operation {
>  #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
> +
> +static int __maybe_unused
> +ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
> +			  enum ieee80211_vht_chanwidth bw,
> +			  int mcs)
> +{
> +	u16 map = le16_to_cpu(cap->supp_mcs.rx_mcs_map);
> +	int max_vht_nss;
> +	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_to_cpu(cap->vht_cap_info) &
> +					IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
> +			>> IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT;
> +	supp_width = (le32_to_cpu(cap->vht_cap_info) &
> +					IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK)
> +			>> IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_SHIFT;
> +
> +	/*
> +	 * 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 == 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 == 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;
> +	}

Looks good to me.

> +	/* not covered or invalid combination received */

Do you want to inform about the invalid/reserved combination.

Regards,
Arend

> +	return max_vht_nss;
> +}
>  
>  /* Authentication algorithms */
>  #define WLAN_AUTH_OPEN 0
>
Johannes Berg Feb. 15, 2017, 9:16 a.m. UTC | #2
On Wed, 2017-02-15 at 10:08 +0100, Arend Van Spriel wrote:
> [snip]
> 
> Looks good to me.

Thanks for checking :)

> > +	/* not covered or invalid combination received */
> 
> Do you want to inform about the invalid/reserved combination.

I'm not really sure what to do - we don't really want to print a
message on something that might have been received from the peer, I
think? Though I suppose we should return 0 for the invalid
combinations, indicating that they're not supported.

johannes
Arend Van Spriel Feb. 15, 2017, 9:36 a.m. UTC | #3
On 15-2-2017 10:16, Johannes Berg wrote:
> On Wed, 2017-02-15 at 10:08 +0100, Arend Van Spriel wrote:
>> [snip]
>>
>> Looks good to me.
> 
> Thanks for checking :)
> 
>>> +	/* not covered or invalid combination received */
>>
>> Do you want to inform about the invalid/reserved combination.
> 
> I'm not really sure what to do - we don't really want to print a
> message on something that might have been received from the peer, I
> think? Though I suppose we should return 0 for the invalid
> combinations, indicating that they're not supported.

Ah. This is all non-functional code yet, right? At least having a static
non-inline function in ieee80211.h will give build issues I would think.
Anyway, I would indeed return 0 and have caller deal with that.

Regards,
Arend
Johannes Berg Feb. 15, 2017, 9:48 a.m. UTC | #4
On Wed, 2017-02-15 at 10:36 +0100, Arend Van Spriel wrote:
> 
> > I'm not really sure what to do - we don't really want to print a
> > message on something that might have been received from the peer, I
> > think? Though I suppose we should return 0 for the invalid
> > combinations, indicating that they're not supported.
> 
> Ah. This is all non-functional code yet, right? At least having a
> static non-inline function in ieee80211.h will give build issues I
> would think.

No, I marked it __maybe_unused so it'll be fine. I didn't want to have
it inlined if you use it multiple times in a single source file, but I
didn't want to move it to somewhere else either ...

> Anyway, I would indeed return 0 and have caller deal with that.

Yeah, I'll do that.

johannes
Arend Van Spriel Feb. 15, 2017, 10:34 a.m. UTC | #5
On 15-2-2017 10:48, Johannes Berg wrote:
> On Wed, 2017-02-15 at 10:36 +0100, Arend Van Spriel wrote:
>>
>>> I'm not really sure what to do - we don't really want to print a
>>> message on something that might have been received from the peer, I
>>> think? Though I suppose we should return 0 for the invalid
>>> combinations, indicating that they're not supported.
>>
>> Ah. This is all non-functional code yet, right? At least having a
>> static non-inline function in ieee80211.h will give build issues I
>> would think.
> 
> No, I marked it __maybe_unused so it'll be fine. I didn't want to have
> it inlined if you use it multiple times in a single source file, but I
> didn't want to move it to somewhere else either ...

Ah. Now I understand the trickery ;-) Was there really no "somewhere
else" to move it, because honestly it is confusing and a bit wasteful if
used multiple times in cfg80211 and/or drivers.

Gr. AvS

>> Anyway, I would indeed return 0 and have caller deal with that.
> 
> Yeah, I'll do that.
> 
> johannes
>
Arend Van Spriel Feb. 15, 2017, 10:34 a.m. UTC | #6
On 15-2-2017 11:34, Arend Van Spriel wrote:
> On 15-2-2017 10:48, Johannes Berg wrote:
>> On Wed, 2017-02-15 at 10:36 +0100, Arend Van Spriel wrote:
>>>
>>>> I'm not really sure what to do - we don't really want to print a
>>>> message on something that might have been received from the peer, I
>>>> think? Though I suppose we should return 0 for the invalid
>>>> combinations, indicating that they're not supported.
>>>
>>> Ah. This is all non-functional code yet, right? At least having a
>>> static non-inline function in ieee80211.h will give build issues I
>>> would think.
>>
>> No, I marked it __maybe_unused so it'll be fine. I didn't want to have
>> it inlined if you use it multiple times in a single source file, but I
>> didn't want to move it to somewhere else either ...
> 
> Ah. Now I understand the trickery ;-) Was there really no "somewhere
> else" to move it, because honestly it is confusing and a bit wasteful if
> used multiple times in cfg80211 and/or drivers.

Although exporting it also comes at a cost.

Gr. AvS

> Gr. AvS
> 
>>> Anyway, I would indeed return 0 and have caller deal with that.
>>
>> Yeah, I'll do that.
>>
>> johannes
>>
Johannes Berg Feb. 15, 2017, 10:41 a.m. UTC | #7
On Wed, 2017-02-15 at 11:34 +0100, Arend Van Spriel wrote:
> On 15-2-2017 11:34, Arend Van Spriel wrote:
> > On 15-2-2017 10:48, Johannes Berg wrote:
> > > On Wed, 2017-02-15 at 10:36 +0100, Arend Van Spriel wrote:
> > > > 
> > > > > I'm not really sure what to do - we don't really want to
> > > > > print a
> > > > > message on something that might have been received from the
> > > > > peer, I
> > > > > think? Though I suppose we should return 0 for the invalid
> > > > > combinations, indicating that they're not supported.
> > > > 
> > > > Ah. This is all non-functional code yet, right? At least having
> > > > a
> > > > static non-inline function in ieee80211.h will give build
> > > > issues I
> > > > would think.
> > > 
> > > No, I marked it __maybe_unused so it'll be fine. I didn't want to
> > > have
> > > it inlined if you use it multiple times in a single source file,
> > > but I
> > > didn't want to move it to somewhere else either ...
> > 
> > Ah. Now I understand the trickery ;-) Was there really no
> > "somewhere
> > else" to move it, because honestly it is confusing and a bit
> > wasteful if
> > used multiple times in cfg80211 and/or drivers.
> 
> Although exporting it also comes at a cost.

Yeah, we could put it into cfg80211 and export it. I haven't really
looked how big it is, but it does seem big.

The other thing is that the ieee80211.h file was pretty much standalone
definitions of the spec until now - if we move it to cfg80211, should
the function prototype really be declared in this file?

johannes
Arend Van Spriel Feb. 15, 2017, 10:54 a.m. UTC | #8
On 15-2-2017 11:41, Johannes Berg wrote:
> On Wed, 2017-02-15 at 11:34 +0100, Arend Van Spriel wrote:
>> On 15-2-2017 11:34, Arend Van Spriel wrote:
>>> On 15-2-2017 10:48, Johannes Berg wrote:
>>>> On Wed, 2017-02-15 at 10:36 +0100, Arend Van Spriel wrote:
>>>>>
>>>>>> I'm not really sure what to do - we don't really want to
>>>>>> print a
>>>>>> message on something that might have been received from the
>>>>>> peer, I
>>>>>> think? Though I suppose we should return 0 for the invalid
>>>>>> combinations, indicating that they're not supported.
>>>>>
>>>>> Ah. This is all non-functional code yet, right? At least having
>>>>> a
>>>>> static non-inline function in ieee80211.h will give build
>>>>> issues I
>>>>> would think.
>>>>
>>>> No, I marked it __maybe_unused so it'll be fine. I didn't want to
>>>> have
>>>> it inlined if you use it multiple times in a single source file,
>>>> but I
>>>> didn't want to move it to somewhere else either ...
>>>
>>> Ah. Now I understand the trickery ;-) Was there really no
>>> "somewhere
>>> else" to move it, because honestly it is confusing and a bit
>>> wasteful if
>>> used multiple times in cfg80211 and/or drivers.
>>
>> Although exporting it also comes at a cost.
> 
> Yeah, we could put it into cfg80211 and export it. I haven't really
> looked how big it is, but it does seem big.
> 
> The other thing is that the ieee80211.h file was pretty much standalone
> definitions of the spec until now - if we move it to cfg80211, should
> the function prototype really be declared in this file?

Well. You can look at it as being close to the spec (and it is), but if
it is a function for cfg80211-based drivers it should go in
include/net/cfg80211.h, right? Not sure though who will be using it
apart from mac80211. So I guess we can do with the trickery. Maybe
clearly mark it with a comment in ieee80211.h.

Regards,
Arend
diff mbox

Patch

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 4a7200c6c9ea..c51d309a45e7 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1457,13 +1457,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;
@@ -1472,6 +1475,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
@@ -1547,6 +1557,7 @@  struct ieee80211_vht_operation {
 #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
@@ -1575,6 +1586,96 @@  struct ieee80211_vht_operation {
 #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
+
+static int __maybe_unused
+ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
+			  enum ieee80211_vht_chanwidth bw,
+			  int mcs)
+{
+	u16 map = le16_to_cpu(cap->supp_mcs.rx_mcs_map);
+	int max_vht_nss;
+	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_to_cpu(cap->vht_cap_info) &
+					IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
+			>> IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT;
+	supp_width = (le32_to_cpu(cap->vht_cap_info) &
+					IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK)
+			>> IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_SHIFT;
+
+	/*
+	 * 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 == 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 == 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;
+}
 
 /* Authentication algorithms */
 #define WLAN_AUTH_OPEN 0