From 35040d1f349f2346832ca1ebb408924dbefc1cfc Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Wed, 14 Jun 2023 07:15:48 +0200
Subject: [PATCH 1/4] mac80211: introduce support for vendor QAM-256 in 2.4GHz
 802.11n

Some vendor supports non-standard QAM-256 in 2.4GHz 802.11n mode.
The implementation works by comunicating vht capabilities to the client
in 2.4GHz 802.11n, the supported client will take this info and
benefits from the additional rates of it.

Each driver needs to enable support for this by enabling the
vendor_qam256_supported in the 2G sband struct and add the required
capabilities for vht_cap.

This feature is supported by various vendor with all kind of marketing
name, but all of them have in common the use of vht capabilities in
2.4GHz 802.11n.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
 include/net/cfg80211.h |  2 ++
 net/mac80211/mlme.c    | 14 ++++++++----
 net/mac80211/util.c    | 10 +++++---
 net/mac80211/vht.c     | 52 +++++++++++++++++++++++++++---------------
 4 files changed, 51 insertions(+), 27 deletions(-)

--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -343,11 +343,13 @@ struct ieee80211_sta_ht_cap {
  * to describe 802.11ac VHT capabilities for an STA.
  *
  * @vht_supported: is VHT supported by the STA
+ * @qam256_supported: is QAM256 supported by the STA
  * @cap: VHT capabilities map as described in 802.11ac spec
  * @vht_mcs: Supported VHT MCS rates
  */
 struct ieee80211_sta_vht_cap {
 	bool vht_supported;
+	bool vendor_qam256_supported;
 	u32 cap; /* use IEEE80211_VHT_CAP_ */
 	struct ieee80211_vht_mcs_info vht_mcs;
 };
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4637,7 +4637,7 @@ static int ieee80211_prep_channel(struct
 	const struct cfg80211_bss_ies *ies;
 	int ret;
 	u32 i;
-	bool have_80mhz;
+	bool can_vht;
 
 	rcu_read_lock();
 
@@ -4770,18 +4770,22 @@ static int ieee80211_prep_channel(struct
 	}
 
 	/* Allow VHT if at least one channel on the sband supports 80 MHz */
-	have_80mhz = false;
+	can_vht = false;
 	for (i = 0; i < sband->n_channels; i++) {
 		if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
 						IEEE80211_CHAN_NO_80MHZ))
 			continue;
 
-		have_80mhz = true;
+		can_vht = true;
 		break;
 	}
 
-	if (!have_80mhz) {
-		sdata_info(sdata, "80 MHz not supported, disabling VHT\n");
+	/* Some Vendor supports non-standard QAM-256 on 2.4GHz 802.11n */
+	if (sband->vht_cap.vendor_qam256_supported)
+		can_vht = true;
+
+	if (!can_vht) {
+		sdata_info(sdata, "80 MHz or QAM-256 not supported, disabling VHT\n");
 		*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
 	}
 
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1843,7 +1843,7 @@ static int ieee80211_build_preq_ies_band
 	int ext_rates_len;
 	int shift;
 	u32 rate_flags;
-	bool have_80mhz = false;
+	bool can_vht = false;
 
 	*offset = 0;
 
@@ -1973,11 +1973,15 @@ static int ieee80211_build_preq_ies_band
 						IEEE80211_CHAN_NO_80MHZ))
 			continue;
 
-		have_80mhz = true;
+		can_vht = true;
 		break;
 	}
 
-	if (sband->vht_cap.vht_supported && have_80mhz) {
+	/* Some Vendor supports non-standard QAM-256 on 2.4GHz 802.11n */
+	if (sband->vht_cap.vendor_qam256_supported)
+		can_vht = true;
+
+	if (sband->vht_cap.vht_supported && can_vht) {
 		if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
 			goto out_err;
 		pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -122,7 +122,7 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(stru
 	struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
 	struct ieee80211_sta_vht_cap own_cap;
 	u32 cap_info, i;
-	bool have_80mhz;
+	bool can_vht;
 	u32 mpdu_len;
 
 	memset(vht_cap, 0, sizeof(*vht_cap));
@@ -134,17 +134,21 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(stru
 		return;
 
 	/* Allow VHT if at least one channel on the sband supports 80 MHz */
-	have_80mhz = false;
+	can_vht = false;
 	for (i = 0; i < sband->n_channels; i++) {
 		if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
 						IEEE80211_CHAN_NO_80MHZ))
 			continue;
 
-		have_80mhz = true;
+		can_vht = true;
 		break;
 	}
 
-	if (!have_80mhz)
+	/* Some Vendor supports non-standard QAM-256 on 2.4GHz 802.11n */
+	if (sband->vht_cap.vendor_qam256_supported)
+		can_vht = true;
+
+	if (!can_vht)
 		return;
 
 	/*
@@ -358,16 +362,16 @@ ieee80211_sta_cap_rx_bw(struct link_sta_
 	struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
 	struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
 	struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
+	struct ieee80211_bss_conf *link_conf;
+	enum ieee80211_sta_rx_bandwidth ret;
 	u32 cap_width;
 
+	rcu_read_lock();
+	link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
+
 	if (he_cap->has_he) {
-		struct ieee80211_bss_conf *link_conf;
-		enum ieee80211_sta_rx_bandwidth ret;
 		u8 info;
 
-		rcu_read_lock();
-		link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
-
 		if (eht_cap->has_eht &&
 		    link_conf->chandef.chan->band == NL80211_BAND_6GHZ) {
 			info = eht_cap->eht_cap_elem.phy_cap_info[0];
@@ -395,32 +399,42 @@ ieee80211_sta_cap_rx_bw(struct link_sta_
 			ret = IEEE80211_STA_RX_BW_80;
 		else
 			ret = IEEE80211_STA_RX_BW_20;
-out:
-		rcu_read_unlock();
 
-		return ret;
+		goto out;
 	}
 
-	if (!vht_cap->vht_supported)
-		return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+	if (!vht_cap->vht_supported ||
+	    (link_conf->chandef.chan->band == NL80211_BAND_2GHZ &&
+	     vht_cap->vendor_qam256_supported)) {
+		ret = link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
 				IEEE80211_STA_RX_BW_40 :
 				IEEE80211_STA_RX_BW_20;
+		goto out;
+	}
 
 	cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
 
 	if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
-	    cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
-		return IEEE80211_STA_RX_BW_160;
+	    cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+		ret = IEEE80211_STA_RX_BW_160;
+		goto out;
+	}
 
 	/*
 	 * If this is non-zero, then it does support 160 MHz after all,
 	 * in one form or the other. We don't distinguish here (or even
 	 * above) between 160 and 80+80 yet.
 	 */
-	if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
-		return IEEE80211_STA_RX_BW_160;
+	if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) {
+		ret = IEEE80211_STA_RX_BW_160;
+		goto out;
+	}
 
-	return IEEE80211_STA_RX_BW_80;
+	ret = IEEE80211_STA_RX_BW_80;
+
+out:
+	rcu_read_unlock();
+	return ret;
 }
 
 enum nl80211_chan_width
