mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-22 11:53:02 +00:00
928 lines
27 KiB
Diff
928 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
|
|
@@ -361,6 +361,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":
|
|
@@ -1520,17 +1532,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,
|
|
@@ -1586,17 +1687,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;
|
|
fallthrough;
|
|
|
|
default:
|
|
@@ -1604,11 +1710,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,
|
|
@@ -1811,6 +1935,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)))
|
|
@@ -1819,10 +1944,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)
|
|
@@ -1850,7 +1977,8 @@ static void ath11k_peer_assoc_h_phymode(
|
|
case NL80211_BAND_5GHZ:
|
|
case NL80211_BAND_6GHZ:
|
|
/* 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)) {
|
|
@@ -2892,6 +3020,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,
|
|
@@ -2937,6 +3079,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,
|
|
@@ -3055,8 +3243,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;
|
|
|
|
@@ -3071,6 +3260,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);
|
|
|
|
@@ -3086,8 +3276,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,
|
|
@@ -3123,6 +3314,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
|
|
@@ -3138,6 +3331,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
|
|
@@ -4734,6 +4930,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);
|
|
@@ -5733,9 +5931,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,
|
|
@@ -5744,8 +5958,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
|
|
@@ -5772,7 +5988,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)
|
|
@@ -5820,7 +6048,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;
|
|
@@ -5831,15 +6060,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);
|
|
@@ -5849,24 +6079,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;
|
|
}
|
|
|
|
@@ -5895,6 +6153,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)
|
|
{
|
|
@@ -5937,10 +6220,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;
|
|
@@ -5951,12 +6236,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
|
|
@@ -5983,8 +6272,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
|
|
@@ -6019,6 +6309,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);
|
|
@@ -6035,7 +6335,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
|
|
@@ -466,6 +466,7 @@ struct ieee80211_supported_band {
|
|
struct ieee80211_sta_ht_cap ht_cap;
|
|
struct ieee80211_sta_vht_cap vht_cap;
|
|
struct ieee80211_sta_s1g_cap s1g_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;
|
|
@@ -679,7 +680,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
|
|
@@ -3187,6 +3187,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
|
|
@@ -4741,6 +4753,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
|
|
*/
|
|
@@ -4750,6 +4766,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,
|
|
@@ -4767,6 +4786,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
|
|
@@ -2843,7 +2843,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;
|
|
}
|
|
@@ -2864,9 +2863,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;
|
|
|
|
@@ -2883,6 +2886,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
|
|
@@ -967,6 +967,9 @@ struct ieee80211_sub_if_data {
|
|
u32 beacon_rateidx_mask[NUM_NL80211_BANDS];
|
|
bool beacon_rate_set;
|
|
|
|
+ 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
|
|
@@ -331,6 +331,80 @@ he_bss_color_policy[NL80211_HE_BSS_COLOR
|
|
[NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG },
|
|
};
|
|
|
|
+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 },
|
|
@@ -338,6 +412,11 @@ static const struct nla_policy nl80211_t
|
|
.len = NL80211_MAX_SUPP_HT_RATES },
|
|
[NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)),
|
|
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
|
+ [NL80211_TXRATE_HE] = {
|
|
+ .type = NLA_EXACT_LEN_WARN,
|
|
+ .len = sizeof(struct nl80211_txrate_he),
|
|
+ },
|
|
+ [NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
|
};
|
|
|
|
static const struct nla_policy
|
|
@@ -4434,7 +4513,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 */
|
|
@@ -4454,6 +4533,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 */
|
|
@@ -4509,13 +4595,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++)
|
|
@@ -4526,6 +4633,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
|
|
@@ -1024,6 +1024,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_6GHZ_PARAMS = 0x99,
|