mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-18 09:51:26 +00:00
This series is based on * 2020-07-10 ipq6018-ilq-11-0_qca_oem-034672b0676c37b1f4519e5720e18e95fe6236ef Add support for * qsdk kernel/v4.4 * qsdk ethernet subsystem * v5.7 ath11k backport + QualComm staging patches (wlan_ap_1.0) * ath11k-firmware * hostapd/iw/... Feature support * full boot, system detection * sysupgrade to nand * HE support via latest hostapd * driver support for usb, crypto, hwmon, cpufreq, ... Missing * NSS/HW flow offloading - FW blob is not redistributable Using the qsdk v4.4 is an intermediate solution while the vanilla is being tested. Vanilla kernel is almost on feature par. Work has already started to upstream the ethernet and switch drivers. Once complete the target will be fully upstream. Signed-off-by: John Crispin <john@phrozen.org>
927 lines
27 KiB
Diff
927 lines
27 KiB
Diff
From 4416e78ba810faa6e1f39f09d4f37e9031ad76cc Mon Sep 17 00:00:00 2001
|
|
From: Miles Hu <milehu@codeaurora.org>
|
|
Date: Tue, 5 Nov 2019 14:42:46 -0800
|
|
Subject: [PATCH] ath11k: add HE fix rate support
|
|
|
|
add HE fix rate/gi/ltf support to nl80211 and driver
|
|
|
|
Signed-off-by: Miles Hu <milehu@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/mac.c | 317 +++++++++++++++++++++++++++++++---
|
|
drivers/net/wireless/ath/ath11k/wmi.h | 1 +
|
|
include/net/cfg80211.h | 4 +
|
|
include/uapi/linux/nl80211.h | 24 +++
|
|
net/mac80211/cfg.c | 12 +-
|
|
net/mac80211/ieee80211_i.h | 3 +
|
|
net/wireless/nl80211.c | 95 +++++++++-
|
|
7 files changed, 433 insertions(+), 23 deletions(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
@@ -254,6 +254,18 @@ ath11k_mac_max_vht_nss(const u16 vht_mcs
|
|
return 1;
|
|
}
|
|
|
|
+static u32
|
|
+ath11k_mac_max_he_nss(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
|
|
+{
|
|
+ int nss;
|
|
+
|
|
+ for (nss = NL80211_HE_NSS_MAX - 1; nss >= 0; nss--)
|
|
+ if (he_mcs_mask[nss])
|
|
+ return nss + 1;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static u8 ath11k_parse_mpdudensity(u8 mpdudensity)
|
|
{
|
|
/* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
|
|
@@ -1129,17 +1141,106 @@ static void ath11k_peer_assoc_h_vht(stru
|
|
/* TODO: rxnss_override */
|
|
}
|
|
|
|
+static int ath11k_mac_get_max_he_mcs_map(u16 mcs_map, int nss)
|
|
+{
|
|
+ switch ((mcs_map >> (2 * nss)) & 0x3) {
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_7: return BIT(8) - 1;
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_9: return BIT(10) - 1;
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_11: return BIT(12) - 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u16
|
|
+ath11k_peer_assoc_h_he_limit(u16 tx_mcs_set,
|
|
+ const u16 he_mcs_limit[NL80211_HE_NSS_MAX])
|
|
+{
|
|
+ int idx_limit;
|
|
+ int nss;
|
|
+ u16 mcs_map;
|
|
+ u16 mcs;
|
|
+
|
|
+ for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) {
|
|
+ mcs_map = ath11k_mac_get_max_he_mcs_map(tx_mcs_set, nss) &
|
|
+ he_mcs_limit[nss];
|
|
+
|
|
+ if (mcs_map)
|
|
+ idx_limit = fls(mcs_map) - 1;
|
|
+ else
|
|
+ idx_limit = -1;
|
|
+
|
|
+ switch (idx_limit) {
|
|
+ case 0: /* fall through */
|
|
+ case 1: /* fall through */
|
|
+ case 2: /* fall through */
|
|
+ case 3: /* fall through */
|
|
+ case 4: /* fall through */
|
|
+ case 5: /* fall through */
|
|
+ case 6: /* fall through */
|
|
+ case 7:
|
|
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_7;
|
|
+ break;
|
|
+ case 8:
|
|
+ case 9:
|
|
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_9;
|
|
+ break;
|
|
+ case 10:
|
|
+ case 11:
|
|
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_11;
|
|
+ break;
|
|
+ default:
|
|
+ WARN_ON(1);
|
|
+ /* fall through */
|
|
+ case -1:
|
|
+ mcs = IEEE80211_HE_MCS_NOT_SUPPORTED;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ tx_mcs_set &= ~(0x3 << (nss * 2));
|
|
+ tx_mcs_set |= mcs << (nss * 2);
|
|
+ }
|
|
+
|
|
+ return tx_mcs_set;
|
|
+}
|
|
+
|
|
+static bool
|
|
+ath11k_peer_assoc_h_he_masked(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
|
|
+{
|
|
+ int nss;
|
|
+
|
|
+ for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++)
|
|
+ if (he_mcs_mask[nss])
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
static void ath11k_peer_assoc_h_he(struct ath11k *ar,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta,
|
|
struct peer_assoc_params *arg)
|
|
{
|
|
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
|
|
+ struct cfg80211_chan_def def;
|
|
const struct ieee80211_sta_he_cap *he_cap = &sta->he_cap;
|
|
- u16 v;
|
|
+ enum nl80211_band band;
|
|
+ const u16 *he_mcs_mask;
|
|
+ u8 max_nss, he_mcs;
|
|
+ __le16 he_tx_mcs = 0, v = 0;
|
|
+ int i;
|
|
+
|
|
+ if (WARN_ON(ath11k_mac_vif_chan(vif, &def)))
|
|
+ return;
|
|
|
|
if (!he_cap->has_he)
|
|
return;
|
|
|
|
+ band = def.chan->band;
|
|
+ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
|
|
+
|
|
+ if (ath11k_peer_assoc_h_he_masked(he_mcs_mask))
|
|
+ return;
|
|
+
|
|
arg->he_flag = true;
|
|
|
|
memcpy(&arg->peer_he_cap_macinfo, he_cap->he_cap_elem.mac_cap_info,
|
|
@@ -1196,17 +1297,22 @@ static void ath11k_peer_assoc_h_he(struc
|
|
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v;
|
|
|
|
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
|
|
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
|
|
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = v;
|
|
|
|
arg->peer_he_mcs_count++;
|
|
+ he_tx_mcs = v;
|
|
}
|
|
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
|
|
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
|
|
|
|
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
|
|
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
|
|
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
|
|
|
|
arg->peer_he_mcs_count++;
|
|
+ if (!he_tx_mcs)
|
|
+ he_tx_mcs = v;
|
|
/* fall through */
|
|
|
|
default:
|
|
@@ -1214,11 +1320,29 @@ static void ath11k_peer_assoc_h_he(struc
|
|
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
|
|
|
|
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
|
|
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
|
|
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
|
|
|
|
arg->peer_he_mcs_count++;
|
|
+ if (!he_tx_mcs)
|
|
+ he_tx_mcs = v;
|
|
break;
|
|
}
|
|
+ /* Calculate peer NSS capability from HE capabilities if STA
|
|
+ * supports HE.
|
|
+ */
|
|
+ for (i = 0, max_nss = 0, he_mcs = 0; i < NL80211_HE_NSS_MAX; i++) {
|
|
+ he_mcs = __le16_to_cpu(he_tx_mcs) >> (2 * i) & 3;
|
|
+
|
|
+ if (he_mcs != IEEE80211_HE_MCS_NOT_SUPPORTED &&
|
|
+ he_mcs_mask[i])
|
|
+ max_nss = i + 1;
|
|
+ }
|
|
+ arg->peer_nss = min(sta->rx_nss, max_nss);
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
|
+ "mac he peer %pM nss %d mcs cnt %d\n",
|
|
+ sta->addr, arg->peer_nss, arg->peer_he_mcs_count);
|
|
}
|
|
|
|
static void ath11k_peer_assoc_h_smps(struct ieee80211_sta *sta,
|
|
@@ -1421,6 +1545,7 @@ static void ath11k_peer_assoc_h_phymode(
|
|
enum nl80211_band band;
|
|
const u8 *ht_mcs_mask;
|
|
const u16 *vht_mcs_mask;
|
|
+ const u16 *he_mcs_mask;
|
|
enum wmi_phy_mode phymode = MODE_UNKNOWN;
|
|
|
|
if (WARN_ON(ath11k_mac_vif_chan(vif, &def)))
|
|
@@ -1429,10 +1554,12 @@ static void ath11k_peer_assoc_h_phymode(
|
|
band = def.chan->band;
|
|
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
|
|
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
|
|
+ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
|
|
|
|
switch (band) {
|
|
case NL80211_BAND_2GHZ:
|
|
- if (sta->he_cap.has_he) {
|
|
+ if (sta->he_cap.has_he &&
|
|
+ !ath11k_peer_assoc_h_he_masked(he_mcs_mask)) {
|
|
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
|
|
phymode = MODE_11AX_HE80_2G;
|
|
else if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
|
|
@@ -1459,7 +1586,8 @@ static void ath11k_peer_assoc_h_phymode(
|
|
break;
|
|
case NL80211_BAND_5GHZ:
|
|
/* Check HE first */
|
|
- if (sta->he_cap.has_he) {
|
|
+ if (sta->he_cap.has_he &&
|
|
+ !ath11k_peer_assoc_h_he_masked(he_mcs_mask)) {
|
|
phymode = ath11k_mac_get_phymode_he(ar, sta);
|
|
} else if (sta->vht_cap.vht_supported &&
|
|
!ath11k_peer_assoc_h_vht_masked(vht_mcs_mask)) {
|
|
@@ -2479,6 +2607,20 @@ ath11k_mac_bitrate_mask_num_vht_rates(st
|
|
}
|
|
|
|
static int
|
|
+ath11k_mac_bitrate_mask_num_he_rates(struct ath11k *ar,
|
|
+ enum nl80211_band band,
|
|
+ const struct cfg80211_bitrate_mask *mask)
|
|
+{
|
|
+ int num_rates = 0;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++)
|
|
+ num_rates += hweight16(mask->control[band].he_mcs[i]);
|
|
+
|
|
+ return num_rates;
|
|
+}
|
|
+
|
|
+static int
|
|
ath11k_mac_set_peer_vht_fixed_rate(struct ath11k_vif *arvif,
|
|
struct ieee80211_sta *sta,
|
|
const struct cfg80211_bitrate_mask *mask,
|
|
@@ -2524,6 +2666,52 @@ ath11k_mac_set_peer_vht_fixed_rate(struc
|
|
return ret;
|
|
}
|
|
|
|
+static int
|
|
+ath11k_mac_set_peer_he_fixed_rate(struct ath11k_vif *arvif,
|
|
+ struct ieee80211_sta *sta,
|
|
+ const struct cfg80211_bitrate_mask *mask,
|
|
+ enum nl80211_band band)
|
|
+{
|
|
+ struct ath11k *ar = arvif->ar;
|
|
+ u8 he_rate, nss;
|
|
+ u32 rate_code;
|
|
+ int ret, i;
|
|
+
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
+
|
|
+ nss = 0;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) {
|
|
+ if (hweight16(mask->control[band].he_mcs[i]) == 1) {
|
|
+ nss = i + 1;
|
|
+ he_rate = ffs(mask->control[band].he_mcs[i]) - 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!nss) {
|
|
+ ath11k_warn(ar->ab, "No single HE Fixed rate found to set for %pM",
|
|
+ sta->addr);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
|
+ "Setting Fixed HE Rate for peer %pM. Device will not switch to any other selected rates",
|
|
+ sta->addr);
|
|
+
|
|
+ rate_code = ATH11K_HW_RATE_CODE(he_rate, nss - 1,
|
|
+ WMI_RATE_PREAMBLE_HE);
|
|
+ ret = ath11k_wmi_set_peer_param(ar, sta->addr,
|
|
+ arvif->vdev_id,
|
|
+ WMI_PEER_PARAM_FIXED_RATE,
|
|
+ rate_code);
|
|
+ if (ret)
|
|
+ ath11k_warn(ar->ab,
|
|
+ "failed to update STA %pM Fixed Rate %d: %d\n",
|
|
+ sta->addr, rate_code, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int ath11k_station_assoc(struct ath11k *ar,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta,
|
|
@@ -2642,8 +2830,9 @@ static void ath11k_sta_rc_update_wk(stru
|
|
enum nl80211_band band;
|
|
const u8 *ht_mcs_mask;
|
|
const u16 *vht_mcs_mask;
|
|
+ const u16 *he_mcs_mask;
|
|
u32 changed, bw, nss, smps;
|
|
- int err, num_vht_rates;
|
|
+ int err, num_vht_rates, num_he_rates;
|
|
const struct cfg80211_bitrate_mask *mask;
|
|
struct peer_assoc_params peer_arg;
|
|
|
|
@@ -2658,6 +2847,7 @@ static void ath11k_sta_rc_update_wk(stru
|
|
band = def.chan->band;
|
|
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
|
|
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
|
|
+ he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
|
|
@@ -2673,8 +2863,9 @@ static void ath11k_sta_rc_update_wk(stru
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
nss = max_t(u32, 1, nss);
|
|
- nss = min(nss, max(ath11k_mac_max_ht_nss(ht_mcs_mask),
|
|
- ath11k_mac_max_vht_nss(vht_mcs_mask)));
|
|
+ nss = min(nss, max(max(ath11k_mac_max_ht_nss(ht_mcs_mask),
|
|
+ ath11k_mac_max_vht_nss(vht_mcs_mask)),
|
|
+ ath11k_mac_max_he_nss(he_mcs_mask)));
|
|
|
|
if (changed & IEEE80211_RC_BW_CHANGED) {
|
|
err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
|
|
@@ -2710,6 +2901,8 @@ static void ath11k_sta_rc_update_wk(stru
|
|
mask = &arvif->bitrate_mask;
|
|
num_vht_rates = ath11k_mac_bitrate_mask_num_vht_rates(ar, band,
|
|
mask);
|
|
+ num_he_rates = ath11k_mac_bitrate_mask_num_he_rates(ar, band,
|
|
+ mask);
|
|
|
|
/* Peer_assoc_prepare will reject vht rates in
|
|
* bitrate_mask if its not available in range format and
|
|
@@ -2725,6 +2918,9 @@ static void ath11k_sta_rc_update_wk(stru
|
|
if (sta->vht_cap.vht_supported && num_vht_rates == 1) {
|
|
ath11k_mac_set_peer_vht_fixed_rate(arvif, sta, mask,
|
|
band);
|
|
+ } else if (sta->he_cap.has_he && num_he_rates == 1) {
|
|
+ ath11k_mac_set_peer_he_fixed_rate(arvif, sta, mask,
|
|
+ band);
|
|
} else {
|
|
/* If the peer is non-VHT or no fixed VHT rate
|
|
* is provided in the new bitrate mask we set the
|
|
@@ -4204,6 +4400,8 @@ static int ath11k_mac_op_add_interface(s
|
|
sizeof(arvif->bitrate_mask.control[i].ht_mcs));
|
|
memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff,
|
|
sizeof(arvif->bitrate_mask.control[i].vht_mcs));
|
|
+ memset(arvif->bitrate_mask.control[i].he_mcs, 0xff,
|
|
+ sizeof(arvif->bitrate_mask.control[i].he_mcs));
|
|
}
|
|
|
|
bit = __ffs64(ab->free_vdev_map);
|
|
@@ -5149,9 +5347,25 @@ ath11k_mac_has_single_legacy_rate(struct
|
|
if (ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask))
|
|
return false;
|
|
|
|
+ if (ath11k_mac_bitrate_mask_num_he_rates(ar, band, mask))
|
|
+ return false;
|
|
+
|
|
return num_rates == 1;
|
|
}
|
|
|
|
+u16 ath11k_mac_get_tx_mcs_map(const struct ieee80211_sta_he_cap *he_cap)
|
|
+{
|
|
+ if (he_cap->he_cap_elem.phy_cap_info[0] &
|
|
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_80p80;
|
|
+
|
|
+ if (he_cap->he_cap_elem.phy_cap_info[0] &
|
|
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_160;
|
|
+
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_80;
|
|
+}
|
|
+
|
|
static bool
|
|
ath11k_mac_bitrate_mask_get_single_nss(struct ath11k *ar,
|
|
enum nl80211_band band,
|
|
@@ -5160,8 +5374,10 @@ ath11k_mac_bitrate_mask_get_single_nss(s
|
|
{
|
|
struct ieee80211_supported_band *sband = &ar->mac.sbands[band];
|
|
u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
|
+ u16 he_mcs_map = 0;
|
|
u8 ht_nss_mask = 0;
|
|
u8 vht_nss_mask = 0;
|
|
+ u8 he_nss_mask = 0;
|
|
int i;
|
|
|
|
/* No need to consider legacy here. Basic rates are always present
|
|
@@ -5188,7 +5404,19 @@ ath11k_mac_bitrate_mask_get_single_nss(s
|
|
return false;
|
|
}
|
|
|
|
- if (ht_nss_mask != vht_nss_mask)
|
|
+ he_mcs_map = le16_to_cpu(ath11k_mac_get_tx_mcs_map(&sband->iftype_data->he_cap));
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) {
|
|
+ if (mask->control[band].he_mcs[i] == 0)
|
|
+ continue;
|
|
+ else if (mask->control[band].he_mcs[i] ==
|
|
+ ath11k_mac_get_max_he_mcs_map(he_mcs_map, i))
|
|
+ he_nss_mask |= BIT(i);
|
|
+ else
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if ((ht_nss_mask != vht_nss_mask) || (ht_nss_mask != he_nss_mask))
|
|
return false;
|
|
|
|
if (ht_nss_mask == 0)
|
|
@@ -5236,7 +5464,8 @@ ath11k_mac_get_single_legacy_rate(struct
|
|
}
|
|
|
|
static int ath11k_mac_set_fixed_rate_params(struct ath11k_vif *arvif,
|
|
- u32 rate, u8 nss, u8 sgi, u8 ldpc)
|
|
+ u32 rate, u8 nss, u8 sgi, u8 ldpc,
|
|
+ u8 he_gi, u8 he_ltf)
|
|
{
|
|
struct ath11k *ar = arvif->ar;
|
|
u32 vdev_param;
|
|
@@ -5247,15 +5476,16 @@ static int ath11k_mac_set_fixed_rate_par
|
|
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac set fixed rate params vdev %i rate 0x%02hhx nss %hhu sgi %hhu\n",
|
|
arvif->vdev_id, rate, nss, sgi);
|
|
|
|
- vdev_param = WMI_VDEV_PARAM_FIXED_RATE;
|
|
- ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
- vdev_param, rate);
|
|
- if (ret) {
|
|
- ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n",
|
|
- rate, ret);
|
|
- return ret;
|
|
+ if (!arvif->vif->bss_conf.he_support) {
|
|
+ vdev_param = WMI_VDEV_PARAM_FIXED_RATE;
|
|
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
+ vdev_param, rate);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to set fixed rate param 0x%02x: %d\n",
|
|
+ rate, ret);
|
|
+ return ret;
|
|
+ }
|
|
}
|
|
-
|
|
vdev_param = WMI_VDEV_PARAM_NSS;
|
|
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
vdev_param, nss);
|
|
@@ -5265,24 +5495,52 @@ static int ath11k_mac_set_fixed_rate_par
|
|
return ret;
|
|
}
|
|
|
|
- vdev_param = WMI_VDEV_PARAM_SGI;
|
|
- ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
- vdev_param, sgi);
|
|
- if (ret) {
|
|
- ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n",
|
|
- sgi, ret);
|
|
- return ret;
|
|
- }
|
|
-
|
|
vdev_param = WMI_VDEV_PARAM_LDPC;
|
|
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
- vdev_param, ldpc);
|
|
+ vdev_param, ldpc);
|
|
if (ret) {
|
|
ath11k_warn(ar->ab, "failed to set ldpc param %d: %d\n",
|
|
- ldpc, ret);
|
|
+ ldpc, ret);
|
|
return ret;
|
|
}
|
|
|
|
+ if (arvif->vif->bss_conf.he_support) {
|
|
+ if (he_gi != 0xFF) {
|
|
+ vdev_param = WMI_VDEV_PARAM_SGI;
|
|
+ /* 0.8 = 0, 1.6 = 2 and 3.2 = 3. */
|
|
+ if (he_gi)
|
|
+ he_gi += 1;
|
|
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
+ vdev_param, he_gi);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to set hegi param %d: %d\n",
|
|
+ sgi, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+ if (he_ltf != 0xFF) {
|
|
+ vdev_param = WMI_VDEV_PARAM_HE_LTF;
|
|
+ /* start from 1 */
|
|
+ he_ltf += 1;
|
|
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
+ vdev_param, he_ltf);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to set heltf param %d: %d\n",
|
|
+ he_ltf, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ vdev_param = WMI_VDEV_PARAM_SGI;
|
|
+ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
|
|
+ vdev_param, sgi);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ar->ab, "failed to set sgi param %d: %d\n",
|
|
+ sgi, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -5311,6 +5569,31 @@ ath11k_mac_vht_mcs_range_present(struct
|
|
return true;
|
|
}
|
|
|
|
+static bool
|
|
+ath11k_mac_he_mcs_range_present(struct ath11k *ar,
|
|
+ enum nl80211_band band,
|
|
+ const struct cfg80211_bitrate_mask *mask)
|
|
+{
|
|
+ int i;
|
|
+ u16 he_mcs;
|
|
+
|
|
+ for (i = 0; i < NL80211_HE_NSS_MAX; i++) {
|
|
+ he_mcs = mask->control[band].he_mcs[i];
|
|
+
|
|
+ switch (he_mcs) {
|
|
+ case 0:
|
|
+ case BIT(8) - 1:
|
|
+ case BIT(10) - 1:
|
|
+ case BIT(12) - 1:
|
|
+ break;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
static void ath11k_mac_set_bitrate_mask_iter(void *data,
|
|
struct ieee80211_sta *sta)
|
|
{
|
|
@@ -5353,10 +5636,12 @@ ath11k_mac_op_set_bitrate_mask(struct ie
|
|
enum nl80211_band band;
|
|
const u8 *ht_mcs_mask;
|
|
const u16 *vht_mcs_mask;
|
|
+ const u16 *he_mcs_mask;
|
|
u32 rate;
|
|
u8 nss;
|
|
u8 sgi;
|
|
u8 ldpc;
|
|
+ u8 he_gi = 0, he_ltf = 0;
|
|
int single_nss;
|
|
int ret;
|
|
int num_rates;
|
|
@@ -5367,12 +5652,16 @@ ath11k_mac_op_set_bitrate_mask(struct ie
|
|
band = def.chan->band;
|
|
ht_mcs_mask = mask->control[band].ht_mcs;
|
|
vht_mcs_mask = mask->control[band].vht_mcs;
|
|
+ he_mcs_mask = mask->control[band].he_mcs;
|
|
ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC);
|
|
|
|
sgi = mask->control[band].gi;
|
|
if (sgi == NL80211_TXRATE_FORCE_LGI)
|
|
return -EINVAL;
|
|
|
|
+ he_gi = mask->control[band].he_gi;
|
|
+ he_ltf = mask->control[band].he_ltf;
|
|
+
|
|
/* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it
|
|
* requires passing atleast one of used basic rates along with them.
|
|
* Fixed rate setting across different preambles(legacy, HT, VHT) is
|
|
@@ -5399,8 +5688,9 @@ ath11k_mac_op_set_bitrate_mask(struct ie
|
|
} else {
|
|
rate = WMI_FIXED_RATE_NONE;
|
|
nss = min_t(u32, ar->num_tx_chains,
|
|
- max(ath11k_mac_max_ht_nss(ht_mcs_mask),
|
|
- ath11k_mac_max_vht_nss(vht_mcs_mask)));
|
|
+ max(max(ath11k_mac_max_ht_nss(ht_mcs_mask),
|
|
+ ath11k_mac_max_vht_nss(vht_mcs_mask)),
|
|
+ ath11k_mac_max_he_nss(he_mcs_mask)));
|
|
|
|
/* If multiple rates across different preambles are given
|
|
* we can reconfigure this info with all peers using PEER_ASSOC
|
|
@@ -5435,6 +5725,16 @@ ath11k_mac_op_set_bitrate_mask(struct ie
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ num_rates = ath11k_mac_bitrate_mask_num_he_rates(ar, band,
|
|
+ mask);
|
|
+
|
|
+ if (!ath11k_mac_he_mcs_range_present(ar, band, mask) &&
|
|
+ num_rates > 1) {
|
|
+ ath11k_warn(ar->ab,
|
|
+ "Setting more than one HE MCS Value in bitrate mask not supported\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
ieee80211_iterate_stations_atomic(ar->hw,
|
|
ath11k_mac_disable_peer_fixed_rate,
|
|
arvif);
|
|
@@ -5451,7 +5751,8 @@ ath11k_mac_op_set_bitrate_mask(struct ie
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
- ret = ath11k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc);
|
|
+ ret = ath11k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc,
|
|
+ he_gi, he_ltf);
|
|
if (ret) {
|
|
ath11k_warn(ar->ab, "failed to set fixed rate params on vdev %i: %d\n",
|
|
arvif->vdev_id, ret);
|
|
--- a/include/net/cfg80211.h
|
|
+++ b/include/net/cfg80211.h
|
|
@@ -416,6 +416,7 @@ struct ieee80211_supported_band {
|
|
int n_bitrates;
|
|
struct ieee80211_sta_ht_cap ht_cap;
|
|
struct ieee80211_sta_vht_cap vht_cap;
|
|
+ struct ieee80211_sta_he_cap he_cap;
|
|
struct ieee80211_edmg edmg_cap;
|
|
u16 n_iftype_data;
|
|
const struct ieee80211_sband_iftype_data *iftype_data;
|
|
@@ -943,7 +944,10 @@ struct cfg80211_bitrate_mask {
|
|
u32 legacy;
|
|
u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
|
|
u16 vht_mcs[NL80211_VHT_NSS_MAX];
|
|
+ u16 he_mcs[NL80211_HE_NSS_MAX];
|
|
enum nl80211_txrate_gi gi;
|
|
+ enum nl80211_he_gi he_gi;
|
|
+ enum nl80211_he_ltf he_ltf;
|
|
} control[NUM_NL80211_BANDS];
|
|
};
|
|
|
|
--- a/include/uapi/linux/nl80211.h
|
|
+++ b/include/uapi/linux/nl80211.h
|
|
@@ -3054,6 +3054,18 @@ enum nl80211_he_gi {
|
|
};
|
|
|
|
/**
|
|
+ * enum nl80211_he_ltf - HE long training field
|
|
+ * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
|
|
+ * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
|
|
+ * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
|
|
+ */
|
|
+enum nl80211_he_ltf {
|
|
+ NL80211_RATE_INFO_HE_1XLTF,
|
|
+ NL80211_RATE_INFO_HE_2XLTF,
|
|
+ NL80211_RATE_INFO_HE_4XLTF,
|
|
+};
|
|
+
|
|
+/**
|
|
* enum nl80211_he_ru_alloc - HE RU allocation values
|
|
* @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation
|
|
* @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation
|
|
@@ -4565,6 +4577,10 @@ enum nl80211_key_attributes {
|
|
* @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
|
|
* see &struct nl80211_txrate_vht
|
|
* @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
|
|
+ * @NL80211_TXRATE_HE: HE rates allowed for TX rate selection,
|
|
+ * see &struct nl80211_txrate_he
|
|
+ * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us.
|
|
+ * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF.
|
|
* @__NL80211_TXRATE_AFTER_LAST: internal
|
|
* @NL80211_TXRATE_MAX: highest TX rate attribute
|
|
*/
|
|
@@ -4574,6 +4590,9 @@ enum nl80211_tx_rate_attributes {
|
|
NL80211_TXRATE_HT,
|
|
NL80211_TXRATE_VHT,
|
|
NL80211_TXRATE_GI,
|
|
+ NL80211_TXRATE_HE,
|
|
+ NL80211_TXRATE_HE_GI,
|
|
+ NL80211_TXRATE_HE_LTF,
|
|
|
|
/* keep last */
|
|
__NL80211_TXRATE_AFTER_LAST,
|
|
@@ -4591,6 +4610,15 @@ struct nl80211_txrate_vht {
|
|
__u16 mcs[NL80211_VHT_NSS_MAX];
|
|
};
|
|
|
|
+#define NL80211_HE_NSS_MAX 8
|
|
+/**
|
|
+ * struct nl80211_txrate_he - HE MCS/NSS txrate bitmap
|
|
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
|
|
+ */
|
|
+struct nl80211_txrate_he {
|
|
+ __u16 mcs[NL80211_HE_NSS_MAX];
|
|
+};
|
|
+
|
|
enum nl80211_txrate_gi {
|
|
NL80211_TXRATE_DEFAULT_GI,
|
|
NL80211_TXRATE_FORCE_SGI,
|
|
--- a/net/mac80211/cfg.c
|
|
+++ b/net/mac80211/cfg.c
|
|
@@ -2882,7 +2882,6 @@ static int ieee80211_set_bitrate_mask(st
|
|
sdata->vif.bss_conf.chandef.chan) {
|
|
u32 basic_rates = sdata->vif.bss_conf.basic_rates;
|
|
enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band;
|
|
-
|
|
if (!(mask->control[band].legacy & basic_rates))
|
|
return -EINVAL;
|
|
}
|
|
@@ -2903,9 +2902,13 @@ static int ieee80211_set_bitrate_mask(st
|
|
memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
|
|
mask->control[i].vht_mcs,
|
|
sizeof(mask->control[i].vht_mcs));
|
|
+ memcpy(sdata->rc_rateidx_he_mcs_mask[i],
|
|
+ mask->control[i].he_mcs,
|
|
+ sizeof(mask->control[i].he_mcs));
|
|
|
|
sdata->rc_has_mcs_mask[i] = false;
|
|
sdata->rc_has_vht_mcs_mask[i] = false;
|
|
+ sdata->rc_has_he_mcs_mask[i] = false;
|
|
if (!sband)
|
|
continue;
|
|
|
|
@@ -2922,6 +2925,13 @@ static int ieee80211_set_bitrate_mask(st
|
|
break;
|
|
}
|
|
}
|
|
+
|
|
+ for (j = 0; j < NL80211_HE_NSS_MAX; j++) {
|
|
+ if (~sdata->rc_rateidx_he_mcs_mask[i][j]) {
|
|
+ sdata->rc_has_he_mcs_mask[i] = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
return 0;
|
|
--- a/net/mac80211/ieee80211_i.h
|
|
+++ b/net/mac80211/ieee80211_i.h
|
|
@@ -963,6 +963,9 @@ struct ieee80211_sub_if_data {
|
|
bool rc_has_vht_mcs_mask[NUM_NL80211_BANDS];
|
|
u16 rc_rateidx_vht_mcs_mask[NUM_NL80211_BANDS][NL80211_VHT_NSS_MAX];
|
|
|
|
+ bool rc_has_he_mcs_mask[NUM_NL80211_BANDS];
|
|
+ u16 rc_rateidx_he_mcs_mask[NUM_NL80211_BANDS][NL80211_HE_NSS_MAX];
|
|
+
|
|
union {
|
|
struct ieee80211_if_ap ap;
|
|
struct ieee80211_if_wds wds;
|
|
--- a/net/wireless/nl80211.c
|
|
+++ b/net/wireless/nl80211.c
|
|
@@ -4228,6 +4228,80 @@ static bool vht_set_mcs_mask(struct ieee
|
|
return true;
|
|
}
|
|
|
|
+static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map)
|
|
+{
|
|
+ switch (he_mcs_map) {
|
|
+ case IEEE80211_HE_MCS_NOT_SUPPORTED:
|
|
+ return 0;
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_7:
|
|
+ return 0x00FF;
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_9:
|
|
+ return 0x03FF;
|
|
+ case IEEE80211_HE_MCS_SUPPORT_0_11:
|
|
+ return 0xFFF;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void he_build_mcs_mask(u16 he_mcs_map,
|
|
+ u16 he_mcs_mask[NL80211_HE_NSS_MAX])
|
|
+{
|
|
+ u8 nss;
|
|
+
|
|
+ for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) {
|
|
+ he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map & 0x03);
|
|
+ he_mcs_map >>= 2;
|
|
+ }
|
|
+}
|
|
+
|
|
+static u16 he_get_txmcsmap(struct genl_info *info,
|
|
+ const struct ieee80211_sta_he_cap *he_cap)
|
|
+{
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
+
|
|
+ switch (wdev->chandef.width) {
|
|
+ case NL80211_CHAN_WIDTH_80P80:
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_80p80;
|
|
+ case NL80211_CHAN_WIDTH_160:
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_160;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return he_cap->he_mcs_nss_supp.tx_mcs_80;
|
|
+}
|
|
+
|
|
+static bool he_set_mcs_mask(struct genl_info *info,
|
|
+ struct ieee80211_supported_band *sband,
|
|
+ struct nl80211_txrate_he *txrate,
|
|
+ u16 mcs[NL80211_HE_NSS_MAX])
|
|
+{
|
|
+ u16 tx_mcs_map = 0;
|
|
+ u16 tx_mcs_mask[NL80211_HE_NSS_MAX] = {};
|
|
+ u8 i;
|
|
+
|
|
+ if (!sband->iftype_data->he_cap.has_he)
|
|
+ return false;
|
|
+
|
|
+ memset(mcs, 0, sizeof(u16) * NL80211_HE_NSS_MAX);
|
|
+
|
|
+ tx_mcs_map = le16_to_cpu(he_get_txmcsmap(info, &sband->iftype_data->he_cap));
|
|
+
|
|
+ /* Build he_mcs_mask from HE capabilities */
|
|
+ he_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
|
|
+
|
|
+ for (i = 0; i < NL80211_HE_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 },
|
|
@@ -4237,6 +4311,10 @@ static const struct nla_policy nl80211_t
|
|
.type = NLA_EXACT_LEN_WARN,
|
|
.len = sizeof(struct nl80211_txrate_vht),
|
|
},
|
|
+ [NL80211_TXRATE_HE] = {
|
|
+ .type = NLA_EXACT_LEN_WARN,
|
|
+ .len = sizeof(struct nl80211_txrate_he),
|
|
+ },
|
|
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
|
};
|
|
|
|
@@ -4248,7 +4326,7 @@ static int nl80211_parse_tx_bitrate_mask
|
|
int rem, i;
|
|
struct nlattr *tx_rates;
|
|
struct ieee80211_supported_band *sband;
|
|
- u16 vht_tx_mcs_map;
|
|
+ u16 vht_tx_mcs_map, he_tx_mcs_map;
|
|
|
|
memset(mask, 0, sizeof(*mask));
|
|
/* Default to all rates enabled */
|
|
@@ -4268,6 +4346,13 @@ static int nl80211_parse_tx_bitrate_mask
|
|
|
|
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);
|
|
+
|
|
+ he_tx_mcs_map = he_get_txmcsmap(info, &sband->iftype_data->he_cap);
|
|
+ he_tx_mcs_map = le16_to_cpu(he_tx_mcs_map);
|
|
+ he_build_mcs_mask(he_tx_mcs_map, mask->control[i].he_mcs);
|
|
+
|
|
+ mask->control[i].he_gi = 0xFF;
|
|
+ mask->control[i].he_ltf = 0xFF;
|
|
}
|
|
|
|
/* if no rates are given set it back to the defaults */
|
|
@@ -4323,13 +4408,34 @@ static int nl80211_parse_tx_bitrate_mask
|
|
if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
|
|
return -EINVAL;
|
|
}
|
|
+ if (tb[NL80211_TXRATE_HE]) {
|
|
+ if (!he_set_mcs_mask(
|
|
+ info,
|
|
+ sband,
|
|
+ nla_data(tb[NL80211_TXRATE_HE]),
|
|
+ mask->control[band].he_mcs))
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (tb[NL80211_TXRATE_HE_GI]) {
|
|
+ mask->control[band].he_gi =
|
|
+ nla_get_u8(tb[NL80211_TXRATE_HE_GI]);
|
|
+ if (mask->control[band].he_gi > NL80211_RATE_INFO_HE_GI_3_2)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (tb[NL80211_TXRATE_HE_LTF]) {
|
|
+ mask->control[band].he_ltf =
|
|
+ nla_get_u8(tb[NL80211_TXRATE_HE_LTF]);
|
|
+ if (mask->control[band].he_ltf > NL80211_RATE_INFO_HE_4XLTF)
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
if (mask->control[band].legacy == 0) {
|
|
/* 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))
|
|
+ rdev->wiphy.bands[band]->vht_cap.vht_supported ||
|
|
+ rdev->wiphy.bands[band]->he_cap.has_he))
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
|
|
@@ -4340,6 +4446,10 @@ static int nl80211_parse_tx_bitrate_mask
|
|
if (mask->control[band].vht_mcs[i])
|
|
goto out;
|
|
|
|
+ for (i = 0; i < NL80211_HE_NSS_MAX; i++)
|
|
+ if (mask->control[band].he_mcs[i])
|
|
+ goto out;
|
|
+
|
|
/* legacy and mcs rates may not be both empty */
|
|
return -EINVAL;
|
|
}
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
|
|
@@ -1015,6 +1015,7 @@ enum wmi_tlv_vdev_param {
|
|
WMI_VDEV_PARAM_HE_RANGE_EXT,
|
|
WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE,
|
|
WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME,
|
|
+ WMI_VDEV_PARAM_HE_LTF = 0x74,
|
|
WMI_VDEV_PARAM_BA_MODE = 0x7e,
|
|
WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87,
|
|
WMI_VDEV_PARAM_PROTOTYPE = 0x8000,
|