// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
staticconstchar *ath12k_mac_phymode_str(enum wmi_phy_mode mode)
{ switch (mode) { case MODE_11A: return"11a"; case MODE_11G: return"11g"; case MODE_11B: return"11b"; case MODE_11GONLY: return"11gonly"; case MODE_11NA_HT20: return"11na-ht20"; case MODE_11NG_HT20: return"11ng-ht20"; case MODE_11NA_HT40: return"11na-ht40"; case MODE_11NG_HT40: return"11ng-ht40"; case MODE_11AC_VHT20: return"11ac-vht20"; case MODE_11AC_VHT40: return"11ac-vht40"; case MODE_11AC_VHT80: return"11ac-vht80"; case MODE_11AC_VHT160: return"11ac-vht160"; case MODE_11AC_VHT80_80: return"11ac-vht80+80"; case MODE_11AC_VHT20_2G: return"11ac-vht20-2g"; case MODE_11AC_VHT40_2G: return"11ac-vht40-2g"; case MODE_11AC_VHT80_2G: return"11ac-vht80-2g"; case MODE_11AX_HE20: return"11ax-he20"; case MODE_11AX_HE40: return"11ax-he40"; case MODE_11AX_HE80: return"11ax-he80"; case MODE_11AX_HE80_80: return"11ax-he80+80"; case MODE_11AX_HE160: return"11ax-he160"; case MODE_11AX_HE20_2G: return"11ax-he20-2g"; case MODE_11AX_HE40_2G: return"11ax-he40-2g"; case MODE_11AX_HE80_2G: return"11ax-he80-2g"; case MODE_11BE_EHT20: return"11be-eht20"; case MODE_11BE_EHT40: return"11be-eht40"; case MODE_11BE_EHT80: return"11be-eht80"; case MODE_11BE_EHT80_80: return"11be-eht80+80"; case MODE_11BE_EHT160: return"11be-eht160"; case MODE_11BE_EHT160_160: return"11be-eht160+160"; case MODE_11BE_EHT320: return"11be-eht320"; case MODE_11BE_EHT20_2G: return"11be-eht20-2g"; case MODE_11BE_EHT40_2G: return"11be-eht40-2g"; case MODE_UNKNOWN: /* skip */ break;
/* no default handler to allow compiler to check that the * enum is fully handled
*/
}
return"";
}
u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones)
{ switch (tones) { case 26: return RU_26; case 52: return RU_52; case 106: return RU_106; case 242: return RU_242; case 484: return RU_484; case 996: return RU_996; case (996 * 2): return RU_2X996; default: return RU_26;
}
}
enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi)
{ switch (sgi) { case RX_MSDU_START_SGI_0_8_US: return NL80211_RATE_INFO_EHT_GI_0_8; case RX_MSDU_START_SGI_1_6_US: return NL80211_RATE_INFO_EHT_GI_1_6; case RX_MSDU_START_SGI_3_2_US: return NL80211_RATE_INFO_EHT_GI_3_2; default: return NL80211_RATE_INFO_EHT_GI_0_8;
}
}
enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones)
{ switch (ru_tones) { case 26: return NL80211_RATE_INFO_EHT_RU_ALLOC_26; case 52: return NL80211_RATE_INFO_EHT_RU_ALLOC_52; case (52 + 26): return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; case 106: return NL80211_RATE_INFO_EHT_RU_ALLOC_106; case (106 + 26): return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; case 242: return NL80211_RATE_INFO_EHT_RU_ALLOC_242; case 484: return NL80211_RATE_INFO_EHT_RU_ALLOC_484; case (484 + 242): return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; case 996: return NL80211_RATE_INFO_EHT_RU_ALLOC_996; case (996 + 484): return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; case (996 + 484 + 242): return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; case (2 * 996): return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; case (2 * 996 + 484): return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; case (3 * 996): return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; case (3 * 996 + 484): return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; case (4 * 996): return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; default: return NL80211_RATE_INFO_EHT_RU_ALLOC_26;
}
}
enum rate_info_bw
ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw)
{
u8 ret = RATE_INFO_BW_20;
switch (bw) { case ATH12K_BW_20:
ret = RATE_INFO_BW_20; break; case ATH12K_BW_40:
ret = RATE_INFO_BW_40; break; case ATH12K_BW_80:
ret = RATE_INFO_BW_80; break; case ATH12K_BW_160:
ret = RATE_INFO_BW_160; break; case ATH12K_BW_320:
ret = RATE_INFO_BW_320; break;
}
return ret;
}
enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw bw)
{ switch (bw) { case RATE_INFO_BW_20: return ATH12K_BW_20; case RATE_INFO_BW_40: return ATH12K_BW_40; case RATE_INFO_BW_80: return ATH12K_BW_80; case RATE_INFO_BW_160: return ATH12K_BW_160; case RATE_INFO_BW_320: return ATH12K_BW_320; default: return ATH12K_BW_20;
}
}
int ath12k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
u16 *rate)
{ /* As default, it is OFDM rates */ int i = ATH12K_MAC_FIRST_OFDM_RATE_IDX; int max_rates_idx = ath12k_g_rates_size;
if (preamble == WMI_RATE_PREAMBLE_CCK) {
hw_rc &= ~ATH12K_HW_RATECODE_CCK_SHORT_PREAM_MASK;
i = 0;
max_rates_idx = ATH12K_MAC_FIRST_OFDM_RATE_IDX;
}
while (i < max_rates_idx) { if (hw_rc == ath12k_legacy_rates[i].hw_value) {
*rateidx = i;
*rate = ath12k_legacy_rates[i].bitrate; return 0;
}
i++;
}
return -EINVAL;
}
u8 ath12k_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
ath12k_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
ath12k_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
ath12k_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 ath12k_parse_mpdudensity(u8 mpdudensity)
{ /* From IEEE Std 802.11-2020 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;
}
}
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf for link %u required to retrieve transmitting link conf\n",
arvif->link_id); return NULL;
} if (link_conf->vif->type == NL80211_IFTYPE_STATION) { if (link_conf->nontransmitted) return link_conf->transmitter_bssid;
} else {
tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); if (tx_arvif) return tx_arvif->bssid;
}
/* To use the arvif returned, caller must have held rcu read lock.
*/
WARN_ON(!rcu_read_lock_any_held());
arvif_iter.vdev_id = vdev_id;
arvif_iter.ar = ar;
flags = IEEE80211_IFACE_ITER_RESUME_ALL;
ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar),
flags,
ath12k_get_arvif_iter,
&arvif_iter); if (!arvif_iter.arvif) {
ath12k_warn(ar->ab, "No VIF found for vdev %d\n", vdev_id); return NULL;
}
if (WARN_ON(pdev_id > ab->num_radios)) return NULL;
for (i = 0; i < ab->num_radios; i++) { if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM)
pdev = &ab->pdevs[i]; else
pdev = rcu_dereference(ab->pdevs_active[i]);
if (!ab->hw_params->single_pdev_only) return ar->pdev->pdev_id;
arvif = ath12k_mac_get_vif_up(ar);
/* fw_pdev array has pdev ids derived from phy capability * service ready event (pdev_and_hw_link_ids). * If no vif is active, return default first index.
*/ if (!arvif) return ar->ab->fw_pdev[0].pdev_id;
/* If active vif is found, return the pdev id matching chandef band */ return ath12k_mac_get_target_pdev_id_from_vif(arvif);
}
/* 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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rts_cts); if (ret)
ath12k_warn(ar->ab, "failed to recalculate rts/cts prot for vdev %d: %d\n",
arvif->vdev_id, ret);
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_STA_KICKOUT_TH,
ATH12K_KICKOUT_THRESHOLD,
ar->pdev->pdev_id); if (ret) {
ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH12K_KEEPALIVE_MIN_IDLE); if (ret) {
ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH12K_KEEPALIVE_MAX_IDLE); if (ret) {
ath12k_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 = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param,
ATH12K_KEEPALIVE_MAX_UNRESPONSIVE); if (ret) {
ath12k_warn(ar->ab, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
vdev_stop:
ret = ath12k_wmi_vdev_stop(ar, vdev_id); if (ret)
ath12k_warn(ar->ab, "failed to stop monitor vdev %i after start failure: %d\n",
vdev_id, ret); return ret;
}
staticint ath12k_mac_monitor_vdev_stop(struct ath12k *ar)
{ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
reinit_completion(&ar->vdev_setup_done);
ret = ath12k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret)
ath12k_warn(ar->ab, "failed to request monitor vdev %i stop: %d\n",
ar->monitor_vdev_id, ret);
ret = ath12k_mac_vdev_setup_sync(ar); if (ret)
ath12k_warn(ar->ab, "failed to synchronize monitor vdev %i stop: %d\n",
ar->monitor_vdev_id, ret);
ret = ath12k_wmi_vdev_down(ar, ar->monitor_vdev_id); if (ret)
ath12k_warn(ar->ab, "failed to put down monitor vdev %i: %d\n",
ar->monitor_vdev_id, ret);
staticint ath12k_mac_monitor_stop(struct ath12k *ar)
{ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
if (!ar->monitor_started) return 0;
ret = ath12k_mac_monitor_vdev_stop(ar); if (ret) {
ath12k_warn(ar->ab, "failed to stop monitor vdev: %d\n", ret); return ret;
}
ar->monitor_started = false;
ar->num_started_vdevs--;
ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, true);
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor stopped ret %d\n", ret); return ret;
}
int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif)
{ struct ath12k_vif *ahvif = arvif->ahvif; struct ath12k *ar = arvif->ar; int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
reinit_completion(&ar->vdev_setup_done);
ret = ath12k_wmi_vdev_stop(ar, arvif->vdev_id); if (ret) {
ath12k_warn(ar->ab, "failed to stop WMI vdev %i: %d\n",
arvif->vdev_id, ret); goto err;
}
ret = ath12k_mac_vdev_setup_sync(ar); if (ret) {
ath12k_warn(ar->ab, "failed to synchronize setup for vdev %i: %d\n",
arvif->vdev_id, ret); goto err;
}
mgmt = (void *)bcn->data;
p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
mgmt->u.beacon.variable,
bcn->len - (mgmt->u.beacon.variable -
bcn->data)); if (!p2p_ie) {
ath12k_warn(ar->ab, "no P2P ie found in beacon\n"); return -ENOENT;
}
ret = ath12k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie); if (ret) {
ath12k_warn(ar->ab, "failed to submit P2P GO bcn ie for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* Return from here for the transmitted profile */ if (!bssid_index) return;
/* Initial rsnie_present for the nontransmitted profile is set to be same as that * of the transmitted profile. It will be changed if security configurations are * different.
*/
*nontx_profile_found = false;
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, rem_len) { /* Fixed minimum MBSSID element length with at least one * nontransmitted BSSID profile is 12 bytes as given below; * 1 (max BSSID indicator) + * 2 (Nontransmitted BSSID profile: Subelement ID + length) + * 4 (Nontransmitted BSSID Capabilities: tag + length + info) * 2 (Nontransmitted BSSID SSID: tag + length) * 3 (Nontransmitted BSSID Index: tag + length + BSSID index
*/ if (elem->datalen < 12 || elem->data[0] < 1) continue; /* Max BSSID indicator must be >=1 */
if (nontx->data[4] != WLAN_EID_SSID) continue; /* Missing SSID for nontransmitted BSS */
index = cfg80211_find_elem(WLAN_EID_MULTI_BSSID_IDX,
start, nontx->datalen); if (!index || index->datalen < 1 || index->data[0] == 0) continue; /* Invalid MBSSID Index element */
if (index->data[0] == bssid_index) {
*nontx_profile_found = true;
/* Check if nontx BSS has beacon protection enabled */ if (!tx_arvif->beacon_prot) {
ext_cap_ie =
cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
nontx->data,
nontx->datalen); if (ext_cap_ie && ext_cap_ie->datalen >= 11 &&
(ext_cap_ie->data[10] &
WLAN_EXT_CAPA11_BCN_PROTECT))
tx_arvif->beacon_prot = true;
}
if (cfg80211_find_ie(WLAN_EID_RSN,
nontx->data,
nontx->datalen)) {
arvif->rsnie_present = true; return;
} elseif (!arvif->rsnie_present) { return; /* Both tx and nontx BSS are open */
}
nie = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
nontx->data,
nontx->datalen); if (!nie || nie->datalen < 2) return; /* Invalid non-inheritance element */
for (i = 1; i < nie->datalen - 1; i++) { if (nie->data[i] == WLAN_EID_RSN) {
arvif->rsnie_present = false; break;
}
}
beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar),
tx_arvif->ahvif->vif,
tx_arvif->link_id); if (!beacons || !beacons->cnt) {
ath12k_warn(arvif->ar->ab, "failed to get ema beacon templates from mac80211\n"); return -EPERM;
}
if (tx_arvif == arvif)
ath12k_mac_set_arvif_ies(arvif, tx_arvif, beacons->bcn[0].skb, 0, NULL);
for (i = 0; i < beacons->cnt; i++) { if (tx_arvif != arvif && !nontx_profile_found)
ath12k_mac_set_arvif_ies(arvif, tx_arvif, beacons->bcn[i].skb,
bssid_index,
&nontx_profile_found);
ema_args.bcn_cnt = beacons->cnt;
ema_args.bcn_index = i;
ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs,
beacons->bcn[i].skb, &ema_args); if (ret) {
ath12k_warn(tx_arvif->ar->ab, "failed to set ema beacon template id %i error %d\n",
i, ret); break;
}
}
if (tx_arvif != arvif && !nontx_profile_found)
ath12k_warn(arvif->ar->ab, "nontransmitted bssid index %u not found in beacon template\n",
bssid_index);
if (ahvif->vdev_type != WMI_VDEV_TYPE_AP) return 0;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf to set bcn tmpl for vif %pM link %u\n",
vif->addr, arvif->link_id); return -ENOLINK;
}
tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); if (tx_arvif) { if (tx_arvif != arvif && arvif->is_up) return 0;
bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar),
tx_arvif->ahvif->vif,
&offs, tx_arvif->link_id); if (!bcn) {
ath12k_warn(ab, "failed to get beacon template from mac80211\n"); return -EPERM;
}
if (tx_arvif == arvif) {
ath12k_mac_set_arvif_ies(arvif, tx_arvif, bcn, 0, NULL);
} else {
ath12k_mac_set_arvif_ies(arvif, tx_arvif, bcn,
link_conf->bssid_index,
&nontx_profile_found); if (!nontx_profile_found)
ath12k_warn(ab, "nontransmitted profile not found in beacon template\n");
}
if (ahvif->vif->type == NL80211_IFTYPE_AP && ahvif->vif->p2p) {
ret = ath12k_mac_setup_bcn_p2p_ie(arvif, bcn); if (ret) {
ath12k_warn(ab, "failed to setup P2P GO bcn ie: %d\n",
ret); goto free_bcn_skb;
}
/* 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 = ath12k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA,
WLAN_OUI_TYPE_WFA_P2P,
offsetof(struct ieee80211_mgmt,
u.beacon.variable)); if (ret) {
ath12k_warn(ab, "failed to remove P2P vendor ie: %d\n",
ret); goto free_bcn_skb;
}
}
ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL);
if (ret)
ath12k_warn(ab, "failed to submit beacon template command: %d\n",
ret);
if (!info->enable_beacon) {
ret = ath12k_wmi_vdev_down(ar, arvif->vdev_id); if (ret)
ath12k_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 = ath12k_mac_setup_bcn_tmpl(arvif); if (ret) {
ath12k_warn(ar->ab, "failed to update bcn tmpl during vdev up: %d\n",
ret); return;
}
ahvif->aid = 0;
ether_addr_copy(arvif->bssid, info->addr);
params.vdev_id = arvif->vdev_id;
params.aid = ahvif->aid;
params.bssid = arvif->bssid;
params.tx_bssid = ath12k_mac_get_tx_bssid(arvif); if (params.tx_bssid) {
params.nontx_profile_idx = info->bssid_index;
params.nontx_profile_cnt = 1 << info->bssid_indicator;
}
ret = ath12k_wmi_vdev_up(arvif->ar, ¶ms); if (ret) {
ath12k_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,
ATH12K_CONNECTION_LOSS_HZ);
}
if (vif->type == NL80211_IFTYPE_STATION)
aid = vif->cfg.aid; else
aid = sta->aid;
ether_addr_copy(arg->peer_mac, arsta->addr);
arg->vdev_id = arvif->vdev_id;
arg->peer_associd = aid;
arg->auth_flag = true; /* TODO: STA WAR in ath10k for listen interval required? */
arg->peer_listen_intval = hw->conf.listen_interval;
arg->peer_nss = 1;
bss_conf = ath12k_mac_get_link_bss_conf(arvif); if (!bss_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc for vif %pM link %u\n",
vif->addr, arvif->link_id); return;
}
info = ath12k_mac_get_link_bss_conf(arvif); if (!info) {
ath12k_warn(ar->ab, "unable to access bss link conf for peer assoc crypto for vif %pM link %u\n",
vif->addr, arvif->link_id); return;
}
if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return;
if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return;
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc rates for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return;
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc ht for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
ht_cap = &link_sta->ht_cap; if (!ht_cap->ht_supported) return;
band = def.chan->band;
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
if (ath12k_peer_assoc_h_ht_masked(ht_mcs_mask)) return;
/* As firmware handles these two flags (IEEE80211_HT_CAP_SGI_20 * and IEEE80211_HT_CAP_SGI_40) for enabling SGI, reset both * flags if guard interval is to force Long GI
*/ if (arvif->bitrate_mask.control[band].gi == NL80211_TXRATE_FORCE_LGI) {
arg->peer_ht_caps &= ~(IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40);
} else { /* Enable SGI flag if either SGI_20 or SGI_40 is supported */ 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(link_sta->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:
ath12k_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:
ath12k_warn(ar->ab, "WMI_NSS_RATIO_2_NSS not supported\n"); break; default:
ath12k_warn(ar->ab, "invalid nss ratio received from fw: %d\n",
nss_ratio_info); break;
}
if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return;
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc vht for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
vht_cap = &link_sta->vht_cap; if (!vht_cap->vht_supported) return;
band = def.chan->band;
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
if (ath12k_peer_assoc_h_vht_masked(vht_mcs_mask)) return;
arg->vht_flag = true;
/* TODO: similar flags required? */
arg->vht_capable = true;
if (def.chan->band == NL80211_BAND_2GHZ)
arg->vht_ng_flag = true;
/* 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 (link_sta->bandwidth == IEEE80211_STA_RX_BW_80)
arg->bw_80 = true;
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160)
arg->bw_160 = true;
vht_nss = ath12k_mac_max_vht_nss(vht_mcs_mask);
if (vht_nss > link_sta->rx_nss) {
user_rate_valid = false; for (nss_idx = link_sta->rx_nss - 1; nss_idx >= 0; nss_idx--) { if (vht_mcs_mask[nss_idx]) {
user_rate_valid = true; break;
}
}
}
if (!user_rate_valid) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Setting vht range MCS value to peer supported nss:%d for peer %pM\n",
link_sta->rx_nss, arsta->addr);
vht_mcs_mask[link_sta->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, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
(2 * i) & 3;
/* In QCN9274 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;
if (WARN_ON(ath12k_mac_vif_link_chan(vif, link_id, &def))) return;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in peer assoc he for vif %pM link %u",
vif->addr, link_id); return;
}
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc he for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
he_cap = &link_sta->he_cap; if (!he_cap->has_he) return;
band = def.chan->band;
he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
if (ath12k_peer_assoc_h_he_masked(he_mcs_mask)) return;
/* 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);
if (support_160) { for (i = 7; i >= 0; i--) {
u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
rx_mcs_160 = i + 1; break;
}
}
}
for (i = 7; i >= 0; i--) {
u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
rx_mcs_80 = i + 1; break;
}
}
if (support_160)
max_nss = min(rx_mcs_80, rx_mcs_160); else
--> --------------------
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.