// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
switch (ru_phy) { case RU_26:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; break; case RU_52:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; break; case RU_106:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; break; case RU_242:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; break; case RU_484:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; break; case RU_996:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; break; default:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; break;
}
switch (ru_tones) { case 26:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; break; case 52:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; break; case 106:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; break; case 242:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; break; case 484:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; break; case 996:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; break; case (996 * 2):
ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; break; default:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; break;
}
switch (sgi) { case RX_MSDU_START_SGI_0_8_US:
ret = NL80211_RATE_INFO_HE_GI_0_8; break; case RX_MSDU_START_SGI_1_6_US:
ret = NL80211_RATE_INFO_HE_GI_1_6; break; case RX_MSDU_START_SGI_3_2_US:
ret = NL80211_RATE_INFO_HE_GI_3_2; break; default:
ret = NL80211_RATE_INFO_HE_GI_0_8; break;
}
return ret;
}
u8 ath11k_mac_bw_to_mac80211_bw(u8 bw)
{
u8 ret = 0;
switch (bw) { case ATH11K_BW_20:
ret = RATE_INFO_BW_20; break; case ATH11K_BW_40:
ret = RATE_INFO_BW_40; break; case ATH11K_BW_80:
ret = RATE_INFO_BW_80; break; case ATH11K_BW_160:
ret = RATE_INFO_BW_160; break;
}
return ret;
}
enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw)
{ switch (bw) { case RATE_INFO_BW_20: return ATH11K_BW_20; case RATE_INFO_BW_40: return ATH11K_BW_40; case RATE_INFO_BW_80: return ATH11K_BW_80; case RATE_INFO_BW_160: return ATH11K_BW_160; default: return ATH11K_BW_20;
}
}
int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
u16 *rate)
{ /* As default, it is OFDM rates */ int i = ATH11K_MAC_FIRST_OFDM_RATE_IDX; int max_rates_idx = ath11k_g_rates_size;
if (preamble == WMI_RATE_PREAMBLE_CCK) {
hw_rc &= ~ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK;
i = 0;
max_rates_idx = ATH11K_MAC_FIRST_OFDM_RATE_IDX;
}
while (i < max_rates_idx) { if (hw_rc == ath11k_legacy_rates[i].hw_value) {
*rateidx = i;
*rate = ath11k_legacy_rates[i].bitrate; return 0;
}
i++;
}
return -EINVAL;
}
staticint get_num_chains(u32 mask)
{ int num_chains = 0;
while (mask) { if (mask & BIT(0))
num_chains++;
mask >>= 1;
}
return num_chains;
}
u8 ath11k_mac_bitrate_to_idx(conststruct ieee80211_supported_band *sband,
u32 bitrate)
{ int i;
for (i = 0; i < sband->n_bitrates; i++) if (sband->bitrates[i].bitrate == bitrate) return i;
return 0;
}
static u32
ath11k_mac_max_ht_nss(const u8 *ht_mcs_mask)
{ int nss;
for (nss = IEEE80211_HT_MCS_MASK_LEN - 1; nss >= 0; nss--) if (ht_mcs_mask[nss]) return nss + 1;
return 1;
}
static u32
ath11k_mac_max_vht_nss(const u16 *vht_mcs_mask)
{ int nss;
for (nss = NL80211_VHT_NSS_MAX - 1; nss >= 0; nss--) if (vht_mcs_mask[nss]) return nss + 1;
return 1;
}
static u32
ath11k_mac_max_he_nss(const u16 *he_mcs_mask)
{ 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": * 0 for no restriction * 1 for 1/4 us * 2 for 1/2 us * 3 for 1 us * 4 for 2 us * 5 for 4 us * 6 for 8 us * 7 for 16 us
*/ switch (mpdudensity) { case 0: return 0; case 1: case 2: case 3: /* Our lower layer calculations limit our precision to * 1 microsecond
*/ return 1; case 4: return 2; case 5: return 4; case 6: return 8; case 7: return 16; default: return 0;
}
}
if (WARN_ON(pdev_id > ab->num_radios)) return NULL;
for (i = 0; i < ab->num_radios; i++) { if (ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM)
pdev = &ab->pdevs[i]; else
pdev = rcu_dereference(ab->pdevs_active[i]);
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
list_for_each_entry(arvif, &ar->arvifs, list) { if (arvif->is_up) return arvif;
}
}
if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) return pdev_id;
band = def.chan->band;
for (i = 0; i < ab->target_pdev_count; i++) { if (ath11k_mac_band_match(band, ab->target_pdev_ids[i].supported_bands)) return ab->target_pdev_ids[i].pdev_id;
}
/* FIXME Set min_tx_power to ab->target_caps.hw_min_tx_power. * But since the received value in svcrdy is same as hw_max_tx_power, * we can set ar->min_tx_power to 0 currently until * this is fixed in firmware
*/
ar->min_tx_power = 0;
/* Enable RTS/CTS protection for sw retries (when legacy stations * are in BSS) or by default only for second rate series. * TODO: Check if we need to enable CTS 2 Self in any case
*/
rts_cts = WMI_USE_RTS_CTS;
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rts_cts); if (ret)
ath11k_warn(ar->ab, "failed to recalculate rts/cts prot for vdev %d: %d\n",
arvif->vdev_id, ret);
ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH,
ATH11K_KICKOUT_THRESHOLD,
ar->pdev->pdev_id); if (ret) {
ath11k_warn(ar->ab, "failed to set kickout threshold on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS;
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH11K_KEEPALIVE_MIN_IDLE); if (ret) {
ath11k_warn(ar->ab, "failed to set keepalive minimum idle time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS;
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH11K_KEEPALIVE_MAX_IDLE); if (ret) {
ath11k_warn(ar->ab, "failed to set keepalive maximum idle time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS;
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH11K_KEEPALIVE_MAX_UNRESPONSIVE); if (ret) {
ath11k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath11k_wmi_vdev_stop(ar, vdev_id); if (ret) {
ath11k_warn(ar->ab, "failed to stop monitor vdev %i after start failure: %d\n",
vdev_id, ret); return ret;
}
ret = ath11k_mac_vdev_setup_sync(ar); if (ret) {
ath11k_warn(ar->ab, "failed to synchronize setup for vdev %i stop: %d\n",
vdev_id, ret); return ret;
}
return -EIO;
}
staticint ath11k_mac_monitor_vdev_stop(struct ath11k *ar)
{ int ret;
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->vdev_setup_done);
ret = ath11k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) {
ath11k_warn(ar->ab, "failed to request monitor vdev %i stop: %d\n",
ar->monitor_vdev_id, ret); return ret;
}
ret = ath11k_mac_vdev_setup_sync(ar); if (ret) {
ath11k_warn(ar->ab, "failed to synchronize monitor vdev %i stop: %d\n",
ar->monitor_vdev_id, ret); return ret;
}
ret = ath11k_wmi_vdev_down(ar, ar->monitor_vdev_id); if (ret) {
ath11k_warn(ar->ab, "failed to put down monitor vdev %i: %d\n",
ar->monitor_vdev_id, ret); return ret;
}
ar->num_started_vdevs++;
ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, false); if (ret) {
ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during start: %d",
ret); return ret;
}
ret = ath11k_dp_tx_htt_monitor_mode_ring_config(ar, true); if (ret) {
ath11k_warn(ar->ab, "failed to configure htt monitor mode ring during stop: %d",
ret); return ret;
}
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "monitor stopped ret %d\n", ret);
ret = ath11k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode); if (ret) {
ath11k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n",
psmode, arvif->vdev_id, ret); return ret;
}
return 0;
}
staticint ath11k_mac_config_ps(struct ath11k *ar)
{ struct ath11k_vif *arvif; int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_mac_vif_setup_ps(arvif); if (ret) {
ath11k_warn(ar->ab, "failed to setup powersave: %d\n", ret); break;
}
}
return ret;
}
staticint ath11k_mac_op_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
{ struct ath11k *ar = hw->priv; struct ieee80211_conf *conf = &hw->conf; int ret = 0;
mutex_lock(&ar->conf_mutex);
if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (conf->flags & IEEE80211_CONF_MONITOR) {
set_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags);
if (test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED,
&ar->monitor_flags)) goto out;
ret = ath11k_mac_monitor_vdev_create(ar); if (ret) {
ath11k_warn(ar->ab, "failed to create monitor vdev: %d",
ret); goto out;
}
ret = ath11k_mac_monitor_start(ar); if (ret) {
ath11k_warn(ar->ab, "failed to start monitor: %d",
ret); goto err_mon_del;
}
} else {
clear_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags);
if (!test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED,
&ar->monitor_flags)) goto out;
ret = ath11k_mac_monitor_stop(ar); if (ret) {
ath11k_warn(ar->ab, "failed to stop monitor: %d",
ret); goto out;
}
ret = ath11k_mac_monitor_vdev_delete(ar); if (ret) {
ath11k_warn(ar->ab, "failed to delete monitor vdev: %d",
ret); goto out;
}
}
}
ret = ath11k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie); if (ret) {
ath11k_warn(ar->ab, "failed to submit P2P GO bcn ie for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) return ret;
ret = ath11k_mac_setup_bcn_p2p_ie(arvif, bcn); if (ret) {
ath11k_warn(ab, "failed to setup P2P GO bcn ie: %d\n",
ret); return ret;
}
/* P2P IE is inserted by firmware automatically (as * configured above) so remove it from the base beacon * template to avoid duplicate P2P IEs in beacon frames.
*/
ret = ath11k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA,
WLAN_OUI_TYPE_WFA_P2P,
offsetof(struct ieee80211_mgmt,
u.beacon.variable)); if (ret) {
ath11k_warn(ab, "failed to remove P2P vendor ie: %d\n",
ret); return ret;
}
staticint ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif, struct ath11k_vif *tx_arvif)
{ struct ieee80211_ema_beacons *beacons; int ret = 0; bool nontx_vif_params_set = false;
u32 params = 0;
u8 i = 0;
beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw,
tx_arvif->vif, 0); if (!beacons || !beacons->cnt) {
ath11k_warn(arvif->ar->ab, "failed to get ema beacon templates from mac80211\n"); return -EPERM;
}
if (tx_arvif == arvif) { if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) return -EINVAL;
} else {
arvif->wpaie_present = tx_arvif->wpaie_present;
}
for (i = 0; i < beacons->cnt; i++) { if (tx_arvif != arvif && !nontx_vif_params_set)
nontx_vif_params_set =
ath11k_mac_set_nontx_vif_params(tx_arvif, arvif,
beacons->bcn[i].skb);
ret = ath11k_wmi_bcn_tmpl(tx_arvif->ar, tx_arvif->vdev_id,
&beacons->bcn[i].offs,
beacons->bcn[i].skb, params); if (ret) {
ath11k_warn(tx_arvif->ar->ab, "failed to set ema beacon template id %i error %d\n",
i, ret); break;
}
}
ieee80211_beacon_free_ema_list(beacons);
if (tx_arvif != arvif && !nontx_vif_params_set) return -EINVAL; /* Profile not found in the beacons */
if (arvif->vdev_type != WMI_VDEV_TYPE_AP) return 0;
/* Target does not expect beacon templates for the already up * non-transmitting interfaces, and results in a crash if sent.
*/
tx_arvif = ath11k_mac_get_tx_arvif(arvif); if (tx_arvif) { if (arvif != tx_arvif && arvif->is_up) return 0;
if (vif->bss_conf.color_change_active)
ieee80211_beacon_update_cntdwn(vif, 0);
ath11k_mac_setup_bcn_tmpl(arvif);
}
staticvoid ath11k_control_beaconing(struct ath11k_vif *arvif, struct ieee80211_bss_conf *info)
{ struct ath11k *ar = arvif->ar; struct ath11k_vif *tx_arvif; int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (!info->enable_beacon) {
ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); if (ret)
ath11k_warn(ar->ab, "failed to down vdev_id %i: %d\n",
arvif->vdev_id, ret);
arvif->is_up = false; return;
}
/* Install the beacon template to the FW */
ret = ath11k_mac_setup_bcn_tmpl(arvif); if (ret) {
ath11k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n",
ret); return;
}
arvif->aid = 0;
ether_addr_copy(arvif->bssid, info->bssid);
tx_arvif = ath11k_mac_get_tx_arvif(arvif);
ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
arvif->bssid,
tx_arvif ? tx_arvif->bssid : NULL,
info->bssid_index,
1 << info->bssid_indicator); if (ret) {
ath11k_warn(ar->ab, "failed to bring up vdev %d: %i\n",
arvif->vdev_id, ret); return;
}
/* Firmware doesn't report beacon loss events repeatedly. If AP probe * (done by mac80211) succeeds but beacons do not resume then it * doesn't make sense to continue operation. Queue connection loss work * which can be cancelled when beacon is received.
*/
ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work,
ATH11K_CONNECTION_LOSS_HZ);
}
/* As firmware handles this two flags (IEEE80211_HT_CAP_SGI_20 * and IEEE80211_HT_CAP_SGI_40) for enabling SGI, we reset * both flags if guard interval is Default GI
*/ if (arvif->bitrate_mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI)
arg->peer_ht_caps &= ~(IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40);
if (arvif->bitrate_mask.control[band].gi != NL80211_TXRATE_FORCE_LGI) { if (ht_cap->cap & (IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40))
arg->peer_rate_caps |= WMI_HOST_RC_SGI_FLAG;
}
for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++) if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) &&
(ht_mcs_mask[i / 8] & BIT(i % 8))) {
max_nss = (i / 8) + 1;
arg->peer_ht_rates.rates[n++] = i;
}
/* This is a workaround for HT-enabled STAs which break the spec * and have no HT capabilities RX mask (no HT RX MCS map). * * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. * * Firmware asserts if such situation occurs.
*/ if (n == 0) {
arg->peer_ht_rates.num_rates = 8; for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
arg->peer_ht_rates.rates[i] = i;
} else {
arg->peer_ht_rates.num_rates = n;
arg->peer_nss = min(sta->deflink.rx_nss, max_nss);
}
switch (idx_limit) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_7; break; case 8:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_8; break; case 9:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; break; default:
WARN_ON(1);
fallthrough; case -1:
mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; break;
}
switch (nss_ratio_info) { case WMI_NSS_RATIO_1BY2_NSS:
max_sup_nss = max_nss >> 1; break; case WMI_NSS_RATIO_3BY4_NSS:
ath11k_warn(ar->ab, "WMI_NSS_RATIO_3BY4_NSS not supported\n"); break; case WMI_NSS_RATIO_1_NSS:
max_sup_nss = max_nss; break; case WMI_NSS_RATIO_2_NSS:
ath11k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n"); break; default:
ath11k_warn(ar->ab, "invalid nss ratio received from firmware: %d\n",
nss_ratio_info); break;
}
/* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to * zero in VHT IE. Using it would result in degraded throughput. * arg->peer_max_mpdu at this point contains HT max_mpdu so keep * it if VHT max_mpdu is smaller.
*/
arg->peer_max_mpdu = max(arg->peer_max_mpdu,
(1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
ampdu_factor)) - 1);
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
arg->bw_80 = true;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
arg->bw_160 = true;
vht_nss = ath11k_mac_max_vht_nss(vht_mcs_mask);
if (vht_nss > sta->deflink.rx_nss) {
user_rate_valid = false; for (nss_idx = sta->deflink.rx_nss - 1; nss_idx >= 0; nss_idx--) { if (vht_mcs_mask[nss_idx]) {
user_rate_valid = true; break;
}
}
}
if (!user_rate_valid) {
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting vht range mcs value to peer supported nss %d for peer %pM\n",
sta->deflink.rx_nss, sta->addr);
vht_mcs_mask[sta->deflink.rx_nss - 1] = vht_mcs_mask[vht_nss - 1];
}
/* Calculate peer NSS capability from VHT capabilities if STA * supports VHT.
*/ for (i = 0, max_nss = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
(2 * i) & 3;
/* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default. * VHT mcs rate 10 and 11 is not supported in 11ac standard. * so explicitly disable the VHT MCS rate 10 and 11 in 11ac mode.
*/
arg->tx_mcs_set &= ~IEEE80211_VHT_MCS_SUPPORT_0_11_MASK;
arg->tx_mcs_set |= IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11;
if ((arg->tx_mcs_set & IEEE80211_VHT_MCS_NOT_SUPPORTED) ==
IEEE80211_VHT_MCS_NOT_SUPPORTED)
arg->peer_vht_caps &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
/* Supported HE-MCS and NSS Set of peer he_cap is intersection with self he_cp */
mcs_160_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
/* Initialize rx_mcs_160 to 9 which is an invalid value */
rx_mcs_160 = 9; if (support_160) { for (i = 7; i >= 0; i--) {
u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
rx_mcs_160 = i + 1; break;
}
}
}
/* Initialize rx_mcs_80 to 9 which is an invalid value */
rx_mcs_80 = 9; for (i = 7; i >= 0; i--) {
u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
rx_mcs_80 = i + 1; break;
}
}
if (support_160)
max_nss = min(rx_mcs_80, rx_mcs_160); else
max_nss = rx_mcs_80;
/* the top most byte is used to indicate BSS color info */
arg->peer_he_ops &= 0xffffff;
/* As per section 26.6.1 11ax Draft5.0, if the Max AMPDU Exponent Extension * in HE cap is zero, use the arg->peer_max_mpdu as calculated while parsing * VHT caps(if VHT caps is present) or HT caps (if VHT caps is not present). * * For non-zero value of Max AMPDU Extponent Extension in HE MAC caps, * if a HE STA sends VHT cap and HE cap IE in assoc request then, use * MAX_AMPDU_LEN_FACTOR as 20 to calculate max_ampdu length. * If a HE STA that does not send VHT cap, but HE and HT cap in assoc * request, then use MAX_AMPDU_LEN_FACTOR as 16 to calculate max_ampdu * length.
*/
ampdu_factor = u8_get_bits(he_cap->he_cap_elem.mac_cap_info[3],
IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK);
for (nss = 0; nss <= arg->peer_ppet.numss_m1; nss++) { for (ru = 0; ru < 4; ru++) {
u32 val = 0; int i;
if ((arg->peer_ppet.ru_bit_mask & BIT(ru)) == 0) continue; for (i = 0; i < 6; i++) {
val >>= 1;
val |= ((he_cap->ppe_thres[bit / 8] >>
(bit % 8)) & 0x1) << 5;
bit++;
}
arg->peer_ppet.ppet16_ppet8_ru3_ru0[nss] |=
val << (ru * 6);
}
}
}
if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_RES)
arg->twt_responder = true; if (he_cap->he_cap_elem.mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_TWT_REQ)
arg->twt_requester = true;
he_nss = ath11k_mac_max_he_nss(he_mcs_mask);
if (he_nss > sta->deflink.rx_nss) {
user_rate_valid = false; for (nss_idx = sta->deflink.rx_nss - 1; nss_idx >= 0; nss_idx--) { if (he_mcs_mask[nss_idx]) {
user_rate_valid = true; break;
}
}
}
if (!user_rate_valid) {
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "setting he range mcs value to peer supported nss %d for peer %pM\n",
sta->deflink.rx_nss, sta->addr);
he_mcs_mask[sta->deflink.rx_nss - 1] = he_mcs_mask[he_nss - 1];
}
switch (sta->deflink.bandwidth) { case IEEE80211_STA_RX_BW_160: if (he_cap->he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
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);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80_80] = 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:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
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; i < NL80211_HE_NSS_MAX; i++) {
he_mcs = he_tx_mcs >> (2 * i) & 3;
/* In case of fixed rates, MCS Range in he_tx_mcs might have * unsupported range, with he_mcs_mask set, so check either of them * to find nss.
*/ if (he_mcs != IEEE80211_HE_MCS_NOT_SUPPORTED ||
he_mcs_mask[i])
max_nss = i + 1;
}
arg->peer_nss = min(sta->deflink.rx_nss, max_nss);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.