// 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"<unknown>";
}
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
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 IEEE Std 802.11ax‐2022, 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 Exponent 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 = ath12k_mac_max_he_nss(he_mcs_mask);
if (he_nss > link_sta->rx_nss) {
user_rate_valid = false; for (nss_idx = link_sta->rx_nss - 1; nss_idx >= 0; nss_idx--) { if (he_mcs_mask[nss_idx]) {
user_rate_valid = true; break;
}
}
}
if (!user_rate_valid) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Setting he range MCS value to peer supported nss:%d for peer %pM\n",
link_sta->rx_nss, arsta->addr);
he_mcs_mask[link_sta->rx_nss - 1] = he_mcs_mask[he_nss - 1];
}
switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160:
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 = ath12k_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 = ath12k_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 = 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;
}
if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def))) return;
band = def.chan->band;
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc he 6ghz for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
he_cap = &link_sta->he_cap;
if (!arg->he_flag || band != NL80211_BAND_6GHZ || !link_sta->he_6ghz_capa.capa) return;
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40)
arg->bw_40 = true;
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;
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_320)
arg->bw_320 = true;
/* From IEEE Std 802.11ax-2021 - Section 10.12.2: An HE STA shall be capable of * receiving A-MPDU where the A-MPDU pre-EOF padding length is up to the value * indicated by the Maximum A-MPDU Length Exponent Extension field in the HE * Capabilities element and the Maximum A-MPDU Length Exponent field in HE 6 GHz * Band Capabilities element in the 6 GHz band. * * Here, we are extracting the Max A-MPDU Exponent Extension from HE caps and * factor is the Maximum A-MPDU Length Exponent from HE 6 GHZ Band capability.
*/
ampdu_factor = u8_get_bits(he_cap->he_cap_elem.mac_cap_info[3],
IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) +
u32_get_bits(arg->peer_he_caps_6ghz,
IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
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;
}
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;
}
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in peer assoc eht for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access link_conf in peer assoc eht set\n"); return;
}
/* For now considering the primary umac based on assoc link */
ml->primary_umac = arsta->is_assoc_link;
ml->peer_id_valid = true;
ml->logical_link_idx_valid = true;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in txbf conf\n"); return -EINVAL;
}
if (!link_conf->he_support) return 0;
if (link_conf->he_su_beamformer) {
value |= u32_encode_bits(HE_SU_BFER_ENABLE, HE_MODE_SU_TX_BFER); if (link_conf->he_mu_beamformer &&
ahvif->vdev_type == WMI_VDEV_TYPE_AP)
value |= u32_encode_bits(HE_MU_BFER_ENABLE, HE_MODE_MU_TX_BFER);
}
if (ahvif->vif->type != NL80211_IFTYPE_MESH_POINT) {
value |= u32_encode_bits(HE_DL_MUOFDMA_ENABLE, HE_MODE_DL_OFDMA) |
u32_encode_bits(HE_UL_MUOFDMA_ENABLE, HE_MODE_UL_OFDMA);
if (link_conf->he_full_ul_mumimo)
value |= u32_encode_bits(HE_UL_MUMIMO_ENABLE, HE_MODE_UL_MUMIMO);
if (link_conf->he_su_beamformee)
value |= u32_encode_bits(HE_SU_BFEE_ENABLE, HE_MODE_SU_TX_BFEE);
}
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, value); if (ret) {
ath12k_warn(ar->ab, "failed to set vdev %d HE MU mode: %d\n",
arvif->vdev_id, ret); return ret;
}
param = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE;
value = u32_encode_bits(HE_VHT_SOUNDING_MODE_ENABLE, HE_VHT_SOUNDING_MODE) |
u32_encode_bits(HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE,
HE_TRIG_NONTRIG_SOUNDING_MODE);
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param, value); if (ret) {
ath12k_warn(ar->ab, "failed to set vdev %d sounding mode: %d\n",
arvif->vdev_id, ret); return ret;
}
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in recalc txbf conf\n"); return -EINVAL;
}
if (!link_conf->he_support) return 0;
if (vif->type != NL80211_IFTYPE_STATION) return -EINVAL;
if (WARN_ON(ath12k_mac_vif_link_chan(vif, link_id, &def))) return -EINVAL;
if (HECAP_PHY_ULMUMIMO_GET(he_cap_elem.phy_cap_info)) if (HECAP_PHY_ULMUMIMO_GET(he_cap->he_cap_elem.phy_cap_info))
*hemode |= u32_encode_bits(HE_UL_MUMIMO_ENABLE,
HE_MODE_UL_MUMIMO);
if (u32_get_bits(*hemode, HE_MODE_MU_TX_BFEE))
*hemode |= u32_encode_bits(HE_SU_BFEE_ENABLE, HE_MODE_SU_TX_BFEE);
if (u32_get_bits(*hemode, HE_MODE_MU_TX_BFER))
*hemode |= u32_encode_bits(HE_SU_BFER_ENABLE, HE_MODE_SU_TX_BFER);
}
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in eht txbf conf\n"); return -ENOENT;
}
if (!link_conf->eht_support) return 0;
if (link_conf->eht_su_beamformer) {
value |= u32_encode_bits(EHT_SU_BFER_ENABLE, EHT_MODE_SU_TX_BFER); if (link_conf->eht_mu_beamformer &&
ahvif->vdev_type == WMI_VDEV_TYPE_AP)
value |= u32_encode_bits(EHT_MU_BFER_ENABLE,
EHT_MODE_MU_TX_BFER) |
u32_encode_bits(EHT_DL_MUOFDMA_ENABLE,
EHT_MODE_DL_OFDMA_MUMIMO) |
u32_encode_bits(EHT_UL_MUOFDMA_ENABLE,
EHT_MODE_UL_OFDMA_MUMIMO);
}
if (ahvif->vif->type != NL80211_IFTYPE_MESH_POINT) {
value |= u32_encode_bits(EHT_DL_MUOFDMA_ENABLE, EHT_MODE_DL_OFDMA) |
u32_encode_bits(EHT_UL_MUOFDMA_ENABLE, EHT_MODE_UL_OFDMA);
if (link_conf->eht_80mhz_full_bw_ul_mumimo)
value |= u32_encode_bits(EHT_UL_MUMIMO_ENABLE, EHT_MODE_MUMIMO);
if (link_conf->eht_su_beamformee)
value |= u32_encode_bits(EHT_SU_BFEE_ENABLE,
EHT_MODE_SU_TX_BFEE);
}
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, value); if (ret) {
ath12k_warn(ar->ab, "failed to set vdev %d EHT MU mode: %d\n",
arvif->vdev_id, ret); return ret;
}
struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) =
kzalloc(sizeof(*peer_arg), GFP_KERNEL); if (!peer_arg) return;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %i link id %u assoc bssid %pM aid %d\n",
arvif->vdev_id, link_id, arvif->bssid, ahvif->aid);
rcu_read_lock();
/* During ML connection, cfg.ap_addr has the MLD address. For * non-ML connection, it has the BSSID.
*/
ap_sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); if (!ap_sta) {
ath12k_warn(ar->ab, "failed to find station entry for bss %pM vdev %i\n",
vif->cfg.ap_addr, arvif->vdev_id);
rcu_read_unlock(); return;
}
ahsta = ath12k_sta_to_ahsta(ap_sta);
arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
ahsta->link[link_id]); if (WARN_ON(!arsta)) {
rcu_read_unlock(); return;
}
link_sta = ath12k_mac_get_link_sta(arsta); if (WARN_ON(!link_sta)) {
rcu_read_unlock(); return;
}
/* link_sta->he_cap must be protected by rcu_read_lock */
ret = ath12k_mac_vif_recalc_sta_he_txbf(ar, arvif, &link_sta->he_cap, &hemode); if (ret) {
ath12k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM: %d\n",
arvif->vdev_id, bss_conf->bssid, ret);
rcu_read_unlock(); return;
}
rcu_read_unlock();
/* keep this before ath12k_wmi_send_peer_assoc_cmd() */
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_SET_HEMU_MODE, hemode); if (ret) {
ath12k_warn(ar->ab, "failed to submit vdev param txbf 0x%x: %d\n",
hemode, ret); return;
}
peer_arg->is_assoc = true;
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (ret) {
ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret); return;
}
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
bss_conf->bssid, arvif->vdev_id); return;
}
ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid,
&link_sta->ht_cap, &link_sta->he_6ghz_capa); if (ret) {
ath12k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n",
arvif->vdev_id, ret); return;
}
/* Authorize BSS Peer */ if (is_auth) {
ret = ath12k_wmi_set_peer_param(ar, arvif->bssid,
arvif->vdev_id,
WMI_PEER_AUTHORIZE,
1); if (ret)
ath12k_warn(ar->ab, "Unable to authorize BSS peer: %d\n", ret);
}
ret = ath12k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id,
&bss_conf->he_obss_pd); if (ret)
ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
arvif->vdev_id, ret);
bss_conf = ath12k_mac_get_link_bss_conf(arvif); if (!bss_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in mgmt rate calc for vif %pM link %u\n",
vif->addr, arvif->link_id); return;
}
hw_rate_code = ath12k_mac_get_rate_hw_value(bitrate); if (hw_rate_code < 0) {
ath12k_warn(ar->ab, "bitrate not supported %d\n", bitrate); return;
}
vdev_param = WMI_VDEV_PARAM_MGMT_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param,
hw_rate_code); if (ret)
ath12k_warn(ar->ab, "failed to set mgmt tx rate %d\n", ret);
vdev_param = WMI_VDEV_PARAM_BEACON_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, vdev_param,
hw_rate_code); if (ret)
ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret);
}
staticvoid ath12k_mac_init_arvif(struct ath12k_vif *ahvif, struct ath12k_link_vif *arvif, int link_id)
{ struct ath12k_hw *ah = ahvif->ah;
u8 _link_id; int i;
lockdep_assert_wiphy(ah->hw->wiphy);
if (WARN_ON(!arvif)) return;
if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) return;
if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); if (ret)
ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d",
arvif->vdev_id, arvif->link_id, ret);
}
ath12k_mac_vdev_delete(ar, arvif);
}
arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); if (arvif) return arvif;
/* If this is the first link arvif being created for an ML VIF * use the preallocated deflink memory except for scan arvifs
*/ if (!ahvif->links_map && link_id < ATH12K_FIRST_SCAN_LINK) {
arvif = &ahvif->deflink;
if (vif->type == NL80211_IFTYPE_STATION)
arvif->is_sta_assoc_link = true;
} else {
arvif = kzalloc(sizeof(*arvif), GFP_KERNEL); if (!arvif) return NULL;
}
ath12k_generic_dbg(ATH12K_DBG_MAC, "mac vif link changed for MLD %pM old_links 0x%x new_links 0x%x\n",
vif->addr, old_links, new_links);
for_each_set_bit(link_id, &to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); /* mac80211 wants to add link but driver already has the * link. This should not happen ideally.
*/ if (WARN_ON(arvif)) return -EINVAL;
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); if (WARN_ON(!arvif)) return -EINVAL;
}
if (changed & BSS_CHANGED_SSID && vif->type == NL80211_IFTYPE_AP) {
ahvif->u.ap.ssid_len = vif->cfg.ssid_len; if (vif->cfg.ssid_len)
memcpy(ahvif->u.ap.ssid, vif->cfg.ssid, vif->cfg.ssid_len);
}
if (changed & BSS_CHANGED_ASSOC) { if (vif->cfg.assoc) { /* only in station mode we can get here, so it's safe * to use ap_addr
*/
rcu_read_lock();
sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); if (!sta) {
rcu_read_unlock();
WARN_ONCE(1, "failed to find sta with addr %pM\n",
vif->cfg.ap_addr); return;
}
ar = arvif->ar; /* there is no reason for which an assoc link's * bss info does not exist
*/
info = ath12k_mac_get_link_bss_conf(arvif);
ath12k_bss_assoc(ar, arvif, info);
/* exclude assoc link as it is done above */
links &= ~BIT(ahsta->assoc_link_id);
}
timeout = conf->dynamic_ps_timeout; if (timeout == 0) {
info = ath12k_mac_get_link_bss_conf(arvif); if (!info) {
ath12k_warn(ar->ab, "unable to access bss link conf in setup ps for vif %pM link %u\n",
vif->addr, arvif->link_id); return;
}
ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
timeout); if (ret) {
ath12k_warn(ar->ab, "failed to set inactivity time for vdev %d: %i\n",
arvif->vdev_id, ret); return;
}
} else {
psmode = WMI_STA_PS_MODE_DISABLED;
}
ret = ath12k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode); if (ret)
ath12k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n",
psmode, arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BEACON_INT) {
arvif->beacon_interval = info->beacon_int;
param_id = WMI_VDEV_PARAM_BEACON_INTERVAL;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id,
arvif->beacon_interval); if (ret)
ath12k_warn(ar->ab, "Failed to set beacon interval for VDEV: %d\n",
arvif->vdev_id); else
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Beacon interval: %d set for VDEV: %d\n",
arvif->beacon_interval, arvif->vdev_id);
}
if (changed & BSS_CHANGED_BEACON) {
param_id = WMI_PDEV_PARAM_BEACON_TX_MODE;
param_value = WMI_BEACON_BURST_MODE;
ret = ath12k_wmi_pdev_set_param(ar, param_id,
param_value, ar->pdev->pdev_id); if (ret)
ath12k_warn(ar->ab, "Failed to set beacon mode for VDEV: %d\n",
arvif->vdev_id); else
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Set burst beacon mode for VDEV: %d\n",
arvif->vdev_id);
ret = ath12k_mac_setup_bcn_tmpl(arvif); if (ret)
ath12k_warn(ar->ab, "failed to update bcn template: %d\n",
ret);
}
if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
arvif->dtim_period = info->dtim_period;
param_id = WMI_VDEV_PARAM_DTIM_PERIOD;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id,
arvif->dtim_period);
if (ret)
ath12k_warn(ar->ab, "Failed to set dtim period for VDEV %d: %i\n",
arvif->vdev_id, ret); else
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "DTIM period: %d set for VDEV: %d\n",
arvif->dtim_period, arvif->vdev_id);
}
if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid))
ether_addr_copy(arvif->bssid, info->bssid);
if (changed & BSS_CHANGED_BEACON_ENABLED) { if (info->enable_beacon) {
ret = ath12k_mac_set_he_txbf_conf(arvif); if (ret)
ath12k_warn(ar->ab, "failed to set HE TXBF config for vdev: %d\n",
arvif->vdev_id);
ret = ath12k_mac_set_eht_txbf_conf(arvif); if (ret)
ath12k_warn(ar->ab, "failed to set EHT TXBF config for vdev: %d\n",
arvif->vdev_id);
}
ath12k_control_beaconing(arvif, info);
if (arvif->is_up && info->he_support &&
info->he_oper.params) { /* TODO: Extend to support 1024 BA Bitmap size */
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_BA_MODE,
WMI_BA_MODE_BUFFER_SIZE_256); if (ret)
ath12k_warn(ar->ab, "failed to set BA BUFFER SIZE 256 for vdev: %d\n",
arvif->vdev_id);
param_id = WMI_VDEV_PARAM_HEOPS_0_31;
param_value = info->he_oper.params;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id, param_value);
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "he oper param: %x set for VDEV: %d\n",
param_value, arvif->vdev_id);
if (ret)
ath12k_warn(ar->ab, "Failed to set he oper params %x for VDEV %d: %i\n",
param_value, arvif->vdev_id, ret);
}
}
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
u32 cts_prot;
vdev_param = WMI_VDEV_PARAM_MCAST_DATA_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rate); if (ret)
ath12k_warn(ar->ab, "failed to set mcast rate on vdev %i: %d\n",
arvif->vdev_id, ret);
vdev_param = WMI_VDEV_PARAM_BCAST_DATA_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rate); if (ret)
ath12k_warn(ar->ab, "failed to set bcast rate on vdev %i: %d\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_TWT) { if (info->twt_requester || info->twt_responder)
ath12k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id); else
ath12k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);
}
if (changed & BSS_CHANGED_HE_OBSS_PD)
ath12k_wmi_send_obss_spr_cmd(ar, arvif->vdev_id,
&info->he_obss_pd);
if (changed & BSS_CHANGED_HE_BSS_COLOR) { if (vif->type == NL80211_IFTYPE_AP) {
ret = ath12k_wmi_obss_color_cfg_cmd(ar,
arvif->vdev_id,
info->he_bss_color.color,
ATH12K_BSS_COLOR_AP_PERIODS,
info->he_bss_color.enabled); if (ret)
ath12k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n",
arvif->vdev_id, ret);
} elseif (vif->type == NL80211_IFTYPE_STATION) {
ret = ath12k_wmi_send_bss_color_change_enable_cmd(ar,
arvif->vdev_id,
1); if (ret)
ath12k_warn(ar->ab, "failed to enable bss color change on vdev %i: %d\n",
arvif->vdev_id, ret);
ret = ath12k_wmi_obss_color_cfg_cmd(ar,
arvif->vdev_id,
0,
ATH12K_BSS_COLOR_STA_PERIODS,
1); if (ret)
ath12k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n",
arvif->vdev_id, ret);
}
}
/* Currently mac80211 supports splitting scan requests into * multiple scan requests per band. * Loop through first channel and determine the scan radio * TODO: There could be 5 GHz low/high channels in that case * split the hw request and perform multiple scans
*/
if (center_freq < ATH12K_MIN_5GHZ_FREQ)
band = NL80211_BAND_2GHZ; elseif (center_freq < ATH12K_MIN_6GHZ_FREQ)
band = NL80211_BAND_5GHZ; else
band = NL80211_BAND_6GHZ;
switch (ar->scan.state) { case ATH12K_SCAN_IDLE: break; case ATH12K_SCAN_RUNNING: case ATH12K_SCAN_ABORTING: if (ar->scan.is_roc && ar->scan.roc_notify)
ieee80211_remain_on_channel_expired(hw);
fallthrough; case ATH12K_SCAN_STARTING:
cancel_delayed_work(&ar->scan.timeout);
complete_all(&ar->scan.completed);
wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk); break;
}
}
/* TODO: Fill other STOP Params */
arg.pdev_id = ar->pdev->pdev_id;
ret = ath12k_wmi_send_scan_stop_cmd(ar, &arg); if (ret) {
ath12k_warn(ar->ab, "failed to stop wmi scan: %d\n", ret); goto out;
}
ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ); if (ret == 0) {
ath12k_warn(ar->ab, "failed to receive scan abort comple: timed out\n");
ret = -ETIMEDOUT;
} elseif (ret > 0) {
ret = 0;
}
out: /* Scan state should be updated in scan completion worker but in * case firmware fails to deliver the event (for whatever reason) * it is desired to clean up scan state anyway. Firmware may have * just dropped the scan completion event delivery due to transport * pipe being overflown with data and/or it can recover on its own * before next scan request is submitted.
*/
spin_lock_bh(&ar->data_lock); if (ret)
__ath12k_mac_scan_finish(ar);
spin_unlock_bh(&ar->data_lock);
return ret;
}
staticvoid ath12k_scan_abort(struct ath12k *ar)
{ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
spin_lock_bh(&ar->data_lock);
switch (ar->scan.state) { case ATH12K_SCAN_IDLE: /* This can happen if timeout worker kicked in and called * abortion while scan completion was being processed.
*/ break; case ATH12K_SCAN_STARTING: case ATH12K_SCAN_ABORTING:
ath12k_warn(ar->ab, "refusing scan abortion due to invalid scan state: %d\n",
ar->scan.state); break; case ATH12K_SCAN_RUNNING:
ar->scan.state = ATH12K_SCAN_ABORTING;
spin_unlock_bh(&ar->data_lock);
ret = ath12k_scan_stop(ar); if (ret)
ath12k_warn(ar->ab, "failed to abort scan: %d\n", ret);
/* The scan vdev has already been deleted. This can occur when a * new scan request is made on the same vif with a different * frequency, causing the scan arvif to move from one radio to * another. Or, scan was abrupted and via remove interface, the * arvif is already deleted. Alternatively, if the scan vdev is not * being used as an actual vdev, then do not delete it.
*/ if (!arvif || arvif->is_started) goto work_complete;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac clean scan vdev (link id %u)",
arvif->link_id);
staticint ath12k_start_scan(struct ath12k *ar, struct ath12k_wmi_scan_req_arg *arg)
{ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
ret = ath12k_wmi_send_scan_start_cmd(ar, arg); if (ret) return ret;
ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ); if (ret == 0) {
ret = ath12k_scan_stop(ar); if (ret)
ath12k_warn(ar->ab, "failed to stop scan: %d\n", ret);
return -ETIMEDOUT;
}
/* If we failed to start the scan, return error code at * this point. This is probably due to some issue in the * firmware, but no need to wedge the driver due to that...
*/
spin_lock_bh(&ar->data_lock); if (ar->scan.state == ATH12K_SCAN_IDLE) {
spin_unlock_bh(&ar->data_lock); return -EINVAL;
}
spin_unlock_bh(&ar->data_lock);
ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
param->vdev_id, param->pdev_id); if (ret) {
ath12k_warn(ab, "failed to request fw stats: %d\n", ret); return ret;
}
ath12k_dbg(ab, ATH12K_DBG_WMI, "get fw stat pdev id %d vdev id %d stats id 0x%x\n",
param->pdev_id, param->vdev_id, param->stats_id);
time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); if (!time_left) {
ath12k_warn(ab, "time out while waiting for get fw stats\n"); return -ETIMEDOUT;
}
/* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back * when stats data buffer limit is reached. fw_stats_complete * is completed once host receives first event from firmware, but * still there could be more events following. Below is to wait * until firmware completes sending all the events.
*/
time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ); if (!time_left) {
ath12k_warn(ab, "time out while waiting for fw stats done\n"); return -ETIMEDOUT;
}
/* Final Tx power is minimum of Target Power, CTL power, Regulatory * Power, PSD EIRP Power. We just know the Regulatory power from the * regulatory rules obtained. FW knows all these power and sets the min * of these. Hence, we request the FW pdev stats in which FW reports * the minimum of all vdev's channel Tx power.
*/
lockdep_assert_wiphy(hw->wiphy);
arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); if (!arvif || !arvif->ar) return -EINVAL;
ar = arvif->ar;
ab = ar->ab; if (ah->state != ATH12K_HW_STATE_ON) goto err_fallback;
if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) return -EAGAIN;
/* Limit the requests to Firmware for fetching the tx power */ if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID &&
time_before(jiffies,
msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) +
ar->last_tx_power_update)) goto send_tx_power;
params.pdev_id = ar->pdev->pdev_id;
params.vdev_id = arvif->vdev_id;
params.stats_id = WMI_REQUEST_PDEV_STAT;
ret = ath12k_mac_get_fw_stats(ar, ¶ms); if (ret) {
ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); goto err_fallback;
}
/* tx power reported by firmware is in units of 0.5 dBm */
ar->chan_tx_pwr = pdev->chan_tx_power / 2;
spin_unlock_bh(&ar->data_lock);
ar->last_tx_power_update = jiffies;
/* input ar is not assigned to any of the links of ML VIF, use next * available scan link for scan vdev creation. There are cases where * single scan req needs to be split in driver and initiate separate * scan requests to firmware based on device.
*/
/* Unset all non-scan links (0-14) of scan_links_map so that ffs() will * choose an available link among scan links (i.e link id >= 15)
*/
scan_links_map = ~ahvif->links_map & ATH12K_SCAN_LINKS_MASK; if (scan_links_map) return __ffs(scan_links_map);
/* check if any of the links of ML VIF is already started on * radio(ar) corresponding to given scan frequency and use it, * if not use scan link (link id >= 15) for scan purpose.
*/
link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar); /* All scan links are occupied. ideally this shouldn't happen as * mac80211 won't schedule scan for same band until ongoing scan is * completed, don't try to exceed max links just in case if it happens.
*/ if (link_id >= ATH12K_NUM_MAX_LINKS) return -EBUSY;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac link ID %d selected for scan",
arvif->link_id);
/* If the vif is already assigned to a specific vdev of an ar, * check whether its already started, vdev which is started * are not allowed to switch to a new radio. * If the vdev is not started, but was earlier created on a * different ar, delete that vdev and create a new one. We don't * delete at the scan stop as an optimization to avoid redundant * delete-create vdev's for the same ar, in case the request is * always on the same band for the vif
*/ if (arvif->is_created) { if (WARN_ON(!arvif->ar)) return -EINVAL;
if (ar != arvif->ar && arvif->is_started) return -EINVAL;
if (ar != arvif->ar) {
ath12k_mac_remove_link_interface(hw, arvif);
ath12k_mac_unassign_link_vif(arvif);
} else {
create = false;
}
}
if (create) { /* Previous arvif would've been cleared in radio switch block * above, assign arvif again for create.
*/
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id);
ret = ath12k_mac_vdev_create(ar, arvif); if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev %d\n", ret); return -EINVAL;
}
}
spin_lock_bh(&ar->data_lock); switch (ar->scan.state) { case ATH12K_SCAN_IDLE:
reinit_completion(&ar->scan.started);
reinit_completion(&ar->scan.completed);
ar->scan.state = ATH12K_SCAN_STARTING;
ar->scan.is_roc = false;
ar->scan.arvif = arvif;
ret = 0; break; case ATH12K_SCAN_STARTING: case ATH12K_SCAN_RUNNING: case ATH12K_SCAN_ABORTING:
ret = -EBUSY; break;
}
spin_unlock_bh(&ar->data_lock);
if (ret) gotoexit;
arg = kzalloc(sizeof(*arg), GFP_KERNEL); if (!arg) {
ret = -ENOMEM; gotoexit;
}
if (req->ie_len) {
arg->extraie.ptr = kmemdup(req->ie, req->ie_len, GFP_KERNEL); if (!arg->extraie.ptr) {
ret = -ENOMEM; gotoexit;
}
arg->extraie.len = req->ie_len;
}
if (req->n_ssids) {
arg->num_ssids = req->n_ssids; for (i = 0; i < arg->num_ssids; i++)
arg->ssid[i] = req->ssids[i];
} else {
arg->scan_f_passive = 1;
}
if (n_channels) {
arg->num_chan = n_channels;
arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list),
GFP_KERNEL); if (!arg->chan_list) {
ret = -ENOMEM; gotoexit;
}
for (i = 0; i < arg->num_chan; i++)
arg->chan_list[i] = chan_list[i]->center_freq;
}
ret = ath12k_start_scan(ar, arg); if (ret) { if (ret == -EBUSY)
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "scan engine is busy 11d state %d\n", ar->state_11d); else
ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
/* Add a margin to account for event/command processing */
ieee80211_queue_delayed_work(ath12k_ar_to_hw(ar), &ar->scan.timeout,
msecs_to_jiffies(arg->max_scan_time +
ATH12K_MAC_SCAN_TIMEOUT_MSECS));
exit: if (arg) {
kfree(arg->chan_list);
kfree(arg->extraie.ptr);
kfree(arg);
}
chan_list = kcalloc(hw_req->req.n_channels, sizeof(*chan_list), GFP_KERNEL); if (!chan_list) return -ENOMEM;
/* There could be channels that belong to multiple underlying radio * in same scan request as mac80211 sees it as single band. In that * case split the hw_req based on frequency range and schedule scans to * corresponding radio.
*/
for_each_ar(ah, ar, i) { int n_chans = 0;
for (j = 0; j < hw_req->req.n_channels; j++) {
chan = hw_req->req.channels[j];
scan_ar = ath12k_mac_select_scan_device(hw, vif,
chan->center_freq); if (!scan_ar) {
ath12k_hw_warn(ah, "unable to select scan device for freq %d\n",
chan->center_freq);
ret = -EINVAL; goto abort;
} if (ar != scan_ar) continue;
chan_list[n_chans++] = chan;
} if (n_chans) {
ret = ath12k_mac_initiate_hw_scan(hw, vif, hw_req, n_chans,
chan_list, ar); if (ret) goto abort;
}
}
abort: /* If any of the parallel scans initiated fails, abort all and * remove the scan interfaces created. Return complete scan * failure as mac80211 assumes this as single scan request.
*/ if (ret) {
ath12k_hw_warn(ah, "Scan failed %d , cleanup all scan vdevs\n", ret);
links_map = ahvif->links_map;
for_each_set_bit(link_id, &links_map, ATH12K_NUM_MAX_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); if (!arvif) continue;
if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) return 0;
if (cmd == DISABLE_KEY) { /* TODO: Check if FW expects value other than NONE for del */ /* arg.key_cipher = WMI_CIPHER_NONE; */
arg.key_len = 0;
arg.key_data = NULL; goto check_order;
}
switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256:
arg.key_cipher = WMI_CIPHER_AES_CCM;
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_TKIP:
arg.key_cipher = WMI_CIPHER_TKIP;
arg.key_txmic_len = 8;
arg.key_rxmic_len = 8; break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
arg.key_cipher = WMI_CIPHER_AES_GCM;
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_AES_CMAC:
arg.key_cipher = WMI_CIPHER_AES_CMAC; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
arg.key_cipher = WMI_CIPHER_AES_GMAC; break; case WLAN_CIPHER_SUITE_BIP_CMAC_256:
arg.key_cipher = WMI_CIPHER_AES_CMAC; break; default:
ath12k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); return -EOPNOTSUPP;
}
if (test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags))
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV |
IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
check_order: if (ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
arg.key_flags == WMI_KEY_GROUP) { if (cmd == SET_KEY) { if (arvif->pairwise_key_done) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "vdev %u pairwise key done, go install group key\n",
arg.vdev_id); goto install;
} else { /* WCN7850 firmware requires pairwise key to be installed * before group key. In case group key comes first, cache * it and return. Will revisit it once pairwise key gets * installed.
*/
arvif->group_key = arg;
arvif->group_key_valid = true;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "vdev %u group key before pairwise key, cache and skip\n",
arg.vdev_id);
out: if (ret) { /* In case of failure userspace may not do DISABLE_KEY * but triggers re-connection directly, so manually reset * status here.
*/
arvif->group_key_valid = false;
arvif->pairwise_key_done = false;
}
for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { if (!peer->keys[i]) continue;
/* key flags are not required to delete the key */
ret = ath12k_install_key(arvif, peer->keys[i],
DISABLE_KEY, addr, flags); if (ret < 0 && first_errno == 0)
first_errno = ret;
if (ret < 0)
ath12k_warn(ab, "failed to remove peer key %d: %d\n",
i, ret);
if (arsta)
sta = ath12k_ahsta_to_sta(arsta->ahsta);
if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) return 1;
if (sta)
peer_addr = arsta->addr; else
peer_addr = arvif->bssid;
key->hw_key_idx = key->keyidx;
/* the peer should not disappear in mid-way (unless FW goes awry) since * we already hold wiphy lock. we just make sure its there now.
*/
spin_lock_bh(&ab->base_lock);
peer = ath12k_peer_find(ab, arvif->vdev_id, peer_addr);
spin_unlock_bh(&ab->base_lock);
if (!peer) { if (cmd == SET_KEY) {
ath12k_warn(ab, "cannot install key for non-existent peer %pM\n",
peer_addr); return -EOPNOTSUPP;
}
/* if the peer doesn't exist there is no key to disable * anymore
*/ return 0;
}
/* IGTK needs to be done in host software */ if (key->keyidx == 4 || key->keyidx == 5) return 1;
if (key->keyidx > WMI_MAX_KEY_INDEX) return -ENOSPC;
if (sta) {
ahsta = ath12k_sta_to_ahsta(sta);
/* For an ML STA Pairwise key is same for all associated link Stations, * hence do set key for all link STAs which are active.
*/ if (sta->mlo) {
links = ahsta->links_map;
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy,
ahvif->link[link_id]);
arsta = wiphy_dereference(hw->wiphy,
ahsta->link[link_id]);
if (WARN_ON(!arvif || !arsta)) /* arvif and arsta are expected to be valid when * STA is present.
*/ continue;
ret = ath12k_mac_set_key(arvif->ar, cmd, arvif,
arsta, key); if (ret) break;
}
return 0;
}
arsta = &ahsta->deflink;
arvif = arsta->arvif; if (WARN_ON(!arvif)) return -EINVAL;
ret = ath12k_mac_set_key(arvif->ar, cmd, arvif, arsta, key); if (ret) return ret;
peer_arg->is_assoc = true;
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (ret) {
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
arsta->addr, arvif->vdev_id, ret); return ret;
}
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
arsta->addr, arvif->vdev_id); return -ETIMEDOUT;
}
/* If single VHT/HE rate is configured (by set_bitrate_mask()), * peer_assoc will disable VHT/HE. This is now enabled by a peer specific * fixed param. * Note that all other rates and NSS will be disabled for this peer.
*/
link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) {
ath12k_warn(ar->ab, "unable to access link sta in station assoc\n"); return -EINVAL;
}
if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) {
ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask, band);
} elseif (link_sta->he_cap.has_he && num_he_rates == 1) {
ret = ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band); if (ret) return ret;
}
/* Re-assoc is run only to update supported rates for given station. It * doesn't make much sense to reconfigure the peer completely.
*/ if (reassoc) return 0;
ret = ath12k_setup_peer_smps(ar, arvif, arsta->addr,
&link_sta->ht_cap, &link_sta->he_6ghz_capa); if (ret) {
ath12k_warn(ar->ab, "failed to setup peer SMPS for vdev %d: %d\n",
arvif->vdev_id, ret); return ret;
}
if (!sta->wme) {
arvif->num_legacy_stations++;
ret = ath12k_recalc_rtscts_prot(arvif); if (ret) return ret;
}
if (sta->wme && sta->uapsd_queues) {
ret = ath12k_peer_assoc_qos_ap(ar, arvif, arsta); if (ret) {
ath12k_warn(ar->ab, "failed to set qos params for STA %pM for vdev %i: %d\n",
arsta->addr, arvif->vdev_id, ret); return ret;
}
}
if (bw > bw_prev) { /* Phymode shows maximum supported channel width, if we * upgrade bandwidth then due to sanity check of firmware, * we have to send WMI_PEER_PHYMODE followed by * WMI_PEER_CHWIDTH
*/
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac bandwidth upgrade for sta %pM new %d old %d\n",
arsta->addr, bw, bw_prev);
err = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id, WMI_PEER_PHYMODE,
peer_phymode); if (err) {
ath12k_warn(ar->ab, "failed to update STA %pM to peer phymode %d: %d\n",
arsta->addr, peer_phymode, err); return;
}
err = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id, WMI_PEER_CHWIDTH,
bw); if (err)
ath12k_warn(ar->ab, "failed to update STA %pM to peer bandwidth %d: %d\n",
arsta->addr, bw, err);
} else { /* When we downgrade bandwidth this will conflict with phymode * and cause to trigger firmware crash. In this case we send * WMI_PEER_CHWIDTH followed by WMI_PEER_PHYMODE
*/
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac bandwidth downgrade for sta %pM new %d old %d\n",
arsta->addr, bw, bw_prev);
err = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id, WMI_PEER_CHWIDTH,
bw); if (err) {
ath12k_warn(ar->ab, "failed to update STA %pM peer to bandwidth %d: %d\n",
arsta->addr, bw, err); return;
}
err = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id, WMI_PEER_PHYMODE,
peer_phymode); if (err)
ath12k_warn(ar->ab, "failed to update STA %pM to peer phymode %d: %d\n",
arsta->addr, peer_phymode, err);
}
}
if (changed & IEEE80211_RC_NSS_CHANGED) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac update sta %pM nss %d\n",
arsta->addr, nss);
err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id,
WMI_PEER_NSS, nss); if (err)
ath12k_warn(ar->ab, "failed to update STA %pM nss %d: %d\n",
arsta->addr, nss, err);
}
if (changed & IEEE80211_RC_SMPS_CHANGED) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac update sta %pM smps %d\n",
arsta->addr, smps);
err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id,
WMI_PEER_MIMO_PS_STATE, smps); if (err)
ath12k_warn(ar->ab, "failed to update STA %pM smps %d: %d\n",
arsta->addr, smps, err);
}
/* Peer_assoc_prepare will reject vht rates in * bitrate_mask if its not available in range format and * sets vht tx_rateset as unsupported. So multiple VHT MCS * setting(eg. MCS 4,5,6) per peer is not supported here. * But, Single rate in VHT mask can be set as per-peer * fixed rate. But even if any HT rates are configured in * the bitrate mask, device will not switch to those rates * when per-peer Fixed rate is set. * TODO: Check RATEMASK_CMDID to support auto rates selection * across HT/VHT and for multiple VHT MCS support.
*/
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;
}
if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) {
ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask,
band);
} elseif (link_sta->he_cap.has_he && num_he_rates == 1) {
ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band);
} else { /* If the peer is non-VHT/HE or no fixed VHT/HE rate * is provided in the new bitrate mask we set the * other rates using peer_assoc command. Also clear * the peer fixed rate settings as it has higher proprity * than peer assoc
*/
err = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id,
WMI_PEER_PARAM_FIXED_RATE,
WMI_FIXED_RATE_NONE); if (err)
ath12k_warn(ar->ab, "failed to disable peer fixed rate for STA %pM ret %d\n",
arsta->addr, err);
peer_arg->is_assoc = false;
err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg); if (err)
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
arsta->addr, arvif->vdev_id, err);
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ))
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
arsta->addr, arvif->vdev_id);
}
}
}
peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); if (peer)
peer->is_authorized = false;
spin_unlock_bh(&ar->ab->base_lock);
/* Driver must clear the keys during the state change from * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC, since after * returning from here, mac80211 is going to delete the keys * in __sta_info_destroy_part2(). This will ensure that the driver does * not retain stale key references after mac80211 deletes the keys.
*/
ret = ath12k_clear_peer_keys(arvif, arsta->addr); if (ret) {
ath12k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath12k_mac_inc_num_stations(arvif, arsta); if (ret) {
ath12k_warn(ab, "refusing to associate station: too many connected already (%d)\n",
ar->max_num_stations); gotoexit;
}
if (ath12k_debugfs_is_extd_rx_stats_enabled(ar) && !arsta->rx_stats) {
arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); if (!arsta->rx_stats) {
ret = -ENOMEM; goto dec_num_station;
}
}
ath12k_dbg(ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n",
arsta->link_id, arsta->addr, old_state, new_state);
/* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: Remove the station * from driver
*/ if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) {
ret = ath12k_mac_station_remove(ar, arvif, arsta); if (ret) {
ath12k_warn(ab, "Failed to remove station: %pM for VDEV: %d\n",
arsta->addr, arvif->vdev_id); gotoexit;
}
}
/* IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE: Add new station to driver */ if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret)
ath12k_warn(ab, "Failed to add station: %pM for VDEV: %d\n",
arsta->addr, arvif->vdev_id);
/* IEEE80211_STA_AUTH -> IEEE80211_STA_ASSOC: Send station assoc command for * peer associated to AP/Mesh/ADHOC vif type.
*/
} elseif (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC &&
(vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
vif->type == NL80211_IFTYPE_ADHOC)) {
ret = ath12k_mac_station_assoc(ar, arvif, arsta, false); if (ret)
ath12k_warn(ab, "Failed to associate station: %pM\n",
arsta->addr);
/* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as * authorized
*/
} elseif (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
ret = ath12k_mac_station_authorize(ar, arvif, arsta); if (ret) {
ath12k_warn(ab, "Failed to authorize station: %pM\n",
arsta->addr); gotoexit;
}
for (i = 0; i < MAX_RADIOS; i++) { if (ath12k_mac_is_freq_on_mac(freq_range, freq_link1, i) &&
ath12k_mac_is_freq_on_mac(freq_range, freq_link2, i)) returntrue;
}
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); /* make sure vdev is created on this device */ if (!arvif || !arvif->is_created || arvif->ar->ab != ab) continue;
info = ath12k_mac_get_link_bss_conf(arvif);
conf = wiphy_dereference(hw->wiphy, info->chanctx_conf);
mlo_freq_list[num_mlo_vdev] = conf->def.chan->center_freq;
/* It is not allowed to activate more links than a single device * supported. Something goes wrong if we reach here.
*/ if (num_mlo_vdev > ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) {
WARN_ON_ONCE(1); return -EINVAL;
}
/* if 2 links are established and both link channels fall on the * same hardware MAC, send command to firmware to deactivate one * of them.
*/ if (num_mlo_vdev == 2 &&
ath12k_mac_freqs_on_same_mac(ab, mlo_freq_list[0],
mlo_freq_list[1])) {
mode = WMI_MLO_LINK_FORCE_MODE_INACTIVE_LINK_NUM;
reason = WMI_MLO_LINK_FORCE_REASON_NEW_CONNECT; return ath12k_mac_mlo_sta_set_link_active(ab, reason, mode,
mlo_vdev_id_lst, num_mlo_vdev,
NULL, 0);
}
/* activate all useful links if less than max supported */ if (num_useful_links <= ATH12K_NUM_MAX_ACTIVE_LINKS_PER_DEVICE) {
*selected_links = useful_links; return 0;
}
/* only in station mode we can get here, so it's safe * to use ap_addr
*/
rcu_read_lock();
sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); if (!sta) {
rcu_read_unlock();
ath12k_warn(ab, "failed to find sta with addr %pM\n", vif->cfg.ap_addr); return -EINVAL;
}
/* assoc link is already activated and has to be kept active, * only need to select a partner link from others.
*/
useful_links &= ~BIT(assoc_arvif->link_id);
for_each_set_bit(link_id, &useful_links, IEEE80211_MLD_MAX_NUM_LINKS) {
info = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); if (!info) {
ath12k_warn(ab, "failed to get link info for link: %u\n",
link_id); return -ENOLINK;
}
chan = info->chanreq.oper.chan; if (!chan) {
ath12k_warn(ab, "failed to get chan for link: %u\n", link_id); return -EINVAL;
}
partner_freq = chan->center_freq; if (ath12k_mac_are_sbs_chan(ab, assoc_link_freq, partner_freq)) {
sbs_links |= BIT(link_id);
ath12k_dbg(ab, ATH12K_DBG_MAC, "new SBS link %u freq %u\n",
link_id, partner_freq); continue;
}
if (ath12k_mac_are_dbs_chan(ab, assoc_link_freq, partner_freq)) {
dbs_links |= BIT(link_id);
ath12k_dbg(ab, ATH12K_DBG_MAC, "new DBS link %u freq %u\n",
link_id, partner_freq); continue;
}
/* choose the first candidate no matter how many is in the list */ if (sbs_links)
link_id = __ffs(sbs_links); elseif (dbs_links)
link_id = __ffs(dbs_links); else
link_id = ffs(useful_links) - 1;
ath12k_dbg(ab, ATH12K_DBG_MAC, "select partner link %u\n", link_id);
/* IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE: * New station add received. If this is a ML station then * ahsta->links_map will be zero and sta->valid_links will be 1. * Assign default link to the first link sta.
*/ if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
memset(ahsta, 0, sizeof(*ahsta));
arsta = &ahsta->deflink;
/* ML sta */ if (sta->mlo && !ahsta->links_map &&
(hweight16(sta->valid_links) == 1)) {
ret = ath12k_peer_ml_create(ah, sta); if (ret) {
ath12k_hw_warn(ah, "unable to create ML peer for sta %pM",
sta->addr); gotoexit;
}
}
ret = ath12k_mac_assign_link_sta(ah, ahsta, arsta, ahvif,
link_id); if (ret) {
ath12k_hw_warn(ah, "unable assign link %d for sta %pM",
link_id, sta->addr); gotoexit;
}
/* above arsta will get memset, hence do this after assign * link sta
*/ if (sta->mlo) { /* For station mode, arvif->is_sta_assoc_link has been set when * vdev starts. Make sure the arvif/arsta pair have same setting
*/ if (vif->type == NL80211_IFTYPE_STATION &&
!arsta->arvif->is_sta_assoc_link) {
ath12k_hw_warn(ah, "failed to verify assoc link setting with link id %u\n",
link_id);
ret = -EINVAL; gotoexit;
}
/* In the ML station scenario, activate all partner links once the * client is transitioning to the associated state. * * FIXME: Ideally, this activation should occur when the client * transitions to the authorized state. However, there are some * issues with handling this in the firmware. Until the firmware * can manage it properly, activate the links when the client is * about to move to the associated state.
*/ if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION &&
old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { /* TODO: for now only do link selection for single device * MLO case. Other cases would be handled in the future.
*/
ab = ah->radio[0].ab; if (ab->ag->num_devices == 1) {
ret = ath12k_mac_select_links(ab, vif, hw, &selected_links); if (ret) {
ath12k_warn(ab, "failed to get selected links: %d\n", ret); gotoexit;
}
} else {
selected_links = ieee80211_vif_usable_links(vif);
}
/* Handle all the other state transitions in generic way */
valid_links = ahsta->links_map;
for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) {
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
arsta = wiphy_dereference(hw->wiphy, ahsta->link[link_id]); /* some assumptions went wrong! */ if (WARN_ON(!arvif || !arsta)) continue;
/* vdev might be in deleted */ if (WARN_ON(!arvif->ar)) continue;
ret = ath12k_mac_handle_link_sta_state(hw, arvif, arsta,
old_state, new_state); if (ret) {
ath12k_hw_warn(ah, "unable to move link sta %d of sta %pM from state %d to %d",
link_id, arsta->addr, old_state, new_state); gotoexit;
}
}
if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION &&
old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) {
for_each_ar(ah, ar, i) {
ab = ar->ab; if (prev_ab == ab) continue;
ret = ath12k_mac_mlo_sta_update_link_active(ab, hw, ahvif); if (ret) {
ath12k_warn(ab, "failed to update link active state on connect %d\n",
ret); gotoexit;
}
prev_ab = ab;
}
} /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: * Remove the station from driver (handle ML sta here since that * needs special handling. Normal sta will be handled in generic * handler below
*/ if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST && sta->mlo)
ath12k_mac_ml_station_remove(ahvif, ahsta);
ret = 0;
exit: /* update the state if everything went well */ if (!ret)
ahsta->state = new_state;
if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) {
txpwr = 0;
} else {
txpwr = sta->deflink.txpwr.power; if (!txpwr) {
ret = -EINVAL; goto out;
}
}
if (txpwr > ATH12K_TX_POWER_MAX_VAL || txpwr < ATH12K_TX_POWER_MIN_VAL) {
ret = -EINVAL; goto out;
}
ar = arvif->ar;
ret = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id,
WMI_PEER_USE_FIXED_PWR, txpwr); if (ret) {
ath12k_warn(ar->ab, "failed to set tx power for station ret: %d\n",
ret); goto out;
}
rcu_read_lock();
arvif = rcu_dereference(ahvif->link[link_sta->link_id]); if (!arvif) {
ath12k_hw_warn(ah, "mac sta rc update failed to fetch link vif on link id %u for peer %pM\n",
link_sta->link_id, sta->addr);
rcu_read_unlock(); return;
}
ar = arvif->ar;
arsta = rcu_dereference(ahsta->link[link_sta->link_id]); if (!arsta) {
rcu_read_unlock();
ath12k_warn(ar->ab, "mac sta rc update failed to fetch link sta on link id %u for peer %pM\n",
link_sta->link_id, sta->addr); return;
}
spin_lock_bh(&ar->ab->base_lock);
peer = ath12k_peer_find(ar->ab, arvif->vdev_id, arsta->addr); if (!peer) {
spin_unlock_bh(&ar->ab->base_lock);
rcu_read_unlock();
ath12k_warn(ar->ab, "mac sta rc update failed to find peer %pM on vdev %i\n",
arsta->addr, arvif->vdev_id); return;
}
spin_unlock_bh(&ar->ab->base_lock);
if (arsta->link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
rcu_read_unlock(); return;
}
link_sta = rcu_dereference(sta->link[arsta->link_id]); if (!link_sta) {
rcu_read_unlock();
ath12k_warn(ar->ab, "unable to access link sta in rc update for sta %pM link %u\n",
sta->addr, arsta->link_id); return;
}
/* Firmware does not support removal of one of link stas. All sta * would be removed during ML STA delete in sta_state(), hence link * sta removal is not handled here.
*/ if (new_links < old_links) return 0;
if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
ath12k_hw_warn(ah, "unable to add link for ml sta %pM", sta->addr); return -EINVAL;
}
/* this op is expected only after initial sta insertion with default link */ if (WARN_ON(ahsta->links_map == 0)) return -EINVAL;
if (ahvif->vdev_type != WMI_VDEV_TYPE_STA) return 0;
switch (ac) { case IEEE80211_AC_VO:
value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; break; case IEEE80211_AC_VI:
value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; break; case IEEE80211_AC_BE:
value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; break; case IEEE80211_AC_BK:
value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; break;
}
if (enable)
ahvif->u.sta.uapsd |= value; else
ahvif->u.sta.uapsd &= ~value;
ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
WMI_STA_PS_PARAM_UAPSD,
ahvif->u.sta.uapsd); if (ret) {
ath12k_warn(ar->ab, "could not set uapsd params %d\n", ret); gotoexit;
}
if (ahvif->u.sta.uapsd)
value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; else
value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
WMI_STA_PS_PARAM_RX_WAKE_POLICY,
value); if (ret)
ath12k_warn(ar->ab, "could not set rx wake param %d\n", ret);
switch (ac) { case IEEE80211_AC_VO:
p = &arvif->wmm_params.ac_vo; break; case IEEE80211_AC_VI:
p = &arvif->wmm_params.ac_vi; break; case IEEE80211_AC_BE:
p = &arvif->wmm_params.ac_be; break; case IEEE80211_AC_BK:
p = &arvif->wmm_params.ac_bk; break;
}
/* TODO: Need to check invalid STS and Sound_dim values set by FW? */
/* Enable Sounding Dimension Field only if SU BF is enabled */ if (subfer) { if (sound_dim > (ar->num_tx_chains - 1))
sound_dim = ar->num_tx_chains - 1;
/* Check if the HW supports 1:1 NSS ratio and reset * EXT NSS BW Support field to 0 to indicate 1:1 ratio
*/ if (ar->pdev->cap.nss_ratio_info == WMI_NSS_RATIO_1_NSS)
vht_cap.cap &= ~IEEE80211_VHT_CAP_EXT_NSS_BW_MASK;
staticint ath12k_check_chain_mask(struct ath12k *ar, u32 ant, bool is_tx_ant)
{ /* TODO: Check the request chainmask against the supported * chainmask table which is advertised in extented_service_ready event
*/
return 0;
}
staticvoid ath12k_gen_ppe_thresh(struct ath12k_wmi_ppe_threshold_arg *fw_ppet,
u8 *he_ppet)
{ int nss, ru;
u8 bit = 7;
for (i = 0; i < 8; i++) { if (i < ar->num_tx_chains &&
(ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
txmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); else
txmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
if (i < ar->num_rx_chains &&
(ar->cfg_rx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
rxmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); else
rxmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
if (i < maxtxnss_160 &&
(ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
txmcs_map_160 |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); else
txmcs_map_160 |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
if (i < maxrxnss_160 &&
(ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
rxmcs_map_160 |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); else
rxmcs_map_160 |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
}
if (ath12k_check_chain_mask(ar, tx_ant, true)) return -EINVAL;
if (ath12k_check_chain_mask(ar, rx_ant, false)) return -EINVAL;
/* Since we advertised the max cap of all radios combined during wiphy * registration, ensure we don't set the antenna config higher than the * limits
*/
tx_ant = min_t(u32, tx_ant, ar->pdev->cap.tx_chain_mask);
rx_ant = min_t(u32, rx_ant, ar->pdev->cap.rx_chain_mask);
/* In case of SW crypto and hdr protected (PMF), packet will already be encrypted, * we can't put in data in this case
*/ if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
has_protected) return 0;
/* FCTL_PROTECTED frame might have extra space added for HDR_LEN. Offset that * many bytes if it is there
*/ if (has_protected) {
skb_cb = ATH12K_SKB_CB(skb);
switch (skb_cb->cipher) { /* Cipher suite having flag %IEEE80211_KEY_FLAG_GENERATE_IV_MGMT set in * key needs to be processed. See ath12k_install_key()
*/ case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
iv_len = IEEE80211_CCMP_HDR_LEN; break; case WLAN_CIPHER_SUITE_TKIP:
iv_len = 0; break; default: return -EINVAL;
}
if (remaining_len < iv_len) return -EINVAL;
buf += iv_len;
remaining_len -= iv_len;
}
category = *buf++; /* category code is already taken care in %IEEE80211_MIN_ACTION_SIZE hence * no need to adjust remaining_len
*/
switch (category) { case WLAN_CATEGORY_RADIO_MEASUREMENT: /* need action code and dialog token */ if (remaining_len < 2) return -EINVAL;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "failed to get bss link conf for vdev %d in RM handling\n",
arvif->vdev_id); return -EINVAL;
}
conf = wiphy_dereference(wiphy, link_conf->chanctx_conf); if (!conf) return -ENOENT;
switch (action_code) { case WLAN_RM_ACTION_LINK_MEASUREMENT_REQUEST: /* need variable fields to be present in len */ if (remaining_len < 2) return -EINVAL;
/* Variable length format as defined in IEEE 802.11-2024, * Figure 9-1187-Link Measurement Request frame Action field * format. * Transmit Power | Max Tx Power * We fill both of these.
*/
*buf++ = cur_tx_power;
*buf = max_tx_power;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "RRM: Link Measurement Req dialog_token %u cur_tx_power %d max_tx_power %d\n",
dialog_token, cur_tx_power, max_tx_power); break; case WLAN_RM_ACTION_LINK_MEASUREMENT_REPORT: /* need variable fields to be present in len */ if (remaining_len < 3) return -EINVAL;
/* Variable length format as defined in IEEE 802.11-2024, * Figure 9-1188-Link Measurement Report frame Action field format * TPC Report | Variable Fields * * TPC Report Format: * Element ID | Len | Tx Power | Link Margin * * We fill Tx power in the TPC Report (2nd index)
*/
buf[2] = cur_tx_power;
/* TODO: At present, Link margin data is not present so can't * really fill it now. Once it is available, it can be added * here
*/
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "RRM: Link Measurement Report dialog_token %u cur_tx_power %d\n",
dialog_token, cur_tx_power); break; default: return -EINVAL;
} break; default: /* nothing to fill */ return 0;
}
while ((skb = skb_dequeue(&ar->wmi_mgmt_tx_queue)) != NULL) {
skb_cb = ATH12K_SKB_CB(skb); if (!skb_cb->vif) {
ath12k_warn(ar->ab, "no vif found for mgmt frame\n");
ath12k_mgmt_over_wmi_tx_drop(ar, skb); continue;
}
ahvif = ath12k_vif_to_ahvif(skb_cb->vif); if (!(ahvif->links_map & BIT(skb_cb->link_id))) {
ath12k_warn(ar->ab, "invalid linkid %u in mgmt over wmi tx with linkmap 0x%x\n",
skb_cb->link_id, ahvif->links_map);
ath12k_mgmt_over_wmi_tx_drop(ar, skb); continue;
}
arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[skb_cb->link_id]); if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) { /* Fill in the data which is required to be filled by the driver * For example: Max Tx power in Link Measurement Request/Report
*/
ret = ath12k_mac_mgmt_frame_fill_elem_data(arvif, skb); if (ret) { /* If we couldn't fill the data due to any reason, * let's not discard transmitting the packet. * For example: Software crypto and PMF case
*/
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "Failed to fill the required data for the mgmt packet err %d\n",
ret);
}
ret = ath12k_mac_mgmt_tx_wmi(ar, arvif, skb); if (ret) {
ath12k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n",
arvif->vdev_id, ret);
ath12k_mgmt_over_wmi_tx_drop(ar, skb);
}
} else {
ath12k_warn(ar->ab, "dropping mgmt frame for vdev %d link %u is_started %d\n",
arvif->vdev_id,
skb_cb->link_id,
arvif->is_started);
ath12k_mgmt_over_wmi_tx_drop(ar, skb);
}
}
}
if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) return -ESHUTDOWN;
/* Drop probe response packets when the pending management tx * count has reached a certain threshold, so as to prioritize * other mgmt packets like auth and assoc to be sent on time * for establishing successful connections.
*/ if (is_prb_rsp &&
atomic_read(&ar->num_pending_mgmt_tx) > ATH12K_PRB_RSP_DROP_THRESHOLD) {
ath12k_warn(ar->ab, "dropping probe response as pending queue is almost full\n"); return -ENOSPC;
}
if (skb_queue_len_lockless(q) >= ATH12K_TX_MGMT_NUM_PENDING_MAX) {
ath12k_warn(ar->ab, "mgmt tx queue is full\n"); return -ENOSPC;
}
/* Use the link id passed or the default vif link */ if (!sta) { if (link != IEEE80211_LINK_UNSPECIFIED) return link;
return ahvif->deflink.link_id;
}
ahsta = ath12k_sta_to_ahsta(sta);
/* Below translation ensures we pass proper A2 & A3 for non ML clients. * Also it assumes for now support only for MLO AP in this path
*/ if (!sta->mlo) {
link = ahsta->deflink.link_id;
if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) return link;
bss_conf = rcu_dereference(vif->link_conf[link]); if (bss_conf) {
ether_addr_copy(hdr->addr2, bss_conf->addr); if (!ieee80211_has_tods(hdr->frame_control) &&
!ieee80211_has_fromds(hdr->frame_control))
ether_addr_copy(hdr->addr3, bss_conf->addr);
}
return link;
}
/* enqueue eth enacap & data frames on primary link, FW does link * selection and address translation.
*/ if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
ieee80211_is_data(hdr->frame_control)) return ahsta->assoc_link_id;
/* 802.11 frame cases */ if (link == IEEE80211_LINK_UNSPECIFIED)
link = ahsta->deflink.link_id;
if (!ieee80211_is_mgmt(hdr->frame_control)) return link;
/* Perform address conversion for ML STA Tx */
bss_conf = rcu_dereference(vif->link_conf[link]);
link_sta = rcu_dereference(sta->link[link]);
if (bss_conf && link_sta) {
ether_addr_copy(hdr->addr1, link_sta->addr);
ether_addr_copy(hdr->addr2, bss_conf->addr);
if (bss_conf) { /* In certain cases where a ML sta associated and added subset of * links on which the ML AP is active, but now sends some frame * (ex. Probe request) on a different link which is active in our * MLD but was not added during previous association, we can * still honor the Tx to that ML STA via the requested link. * The control would reach here in such case only when that link * address is same as the MLD address or in worst case clients * used MLD address at TA wrongly which would have helped * identify the ML sta object and pass it here. * If the link address of that STA is different from MLD address, * then the sta object would be NULL and control won't reach * here but return at the start of the function itself with !sta * check. Also this would not need any translation at hdr->addr1 * from MLD to link address since the RA is the MLD address * (same as that link address ideally) already.
*/
ether_addr_copy(hdr->addr2, bss_conf->addr);
if (key) {
skb_cb->cipher = key->cipher;
skb_cb->flags |= ATH12K_SKB_CIPHER_SET;
}
/* handle only for MLO case, use deflink for non MLO case */ if (ieee80211_vif_is_mld(vif)) {
link_id = ath12k_mac_get_tx_link(sta, vif, link_id, skb, info_flags); if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
ieee80211_free_txskb(hw, skb); return;
}
} else {
link_id = 0;
}
arvif = rcu_dereference(ahvif->link[link_id]); if (!arvif || !arvif->ar) {
ath12k_warn(ahvif->ah, "failed to find arvif link id %u for frame transmission",
link_id);
ieee80211_free_txskb(hw, skb); return;
}
ar = arvif->ar;
skb_cb->link_id = link_id;
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
eth = (struct ethhdr *)skb->data;
is_mcast = is_multicast_ether_addr(eth->h_dest);
ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp); if (ret) {
ath12k_warn(ar->ab, "failed to queue management frame %d\n",
ret);
ieee80211_free_txskb(hw, skb);
} return;
}
if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
is_mcast = is_multicast_ether_addr(hdr->addr1);
/* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p)
ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp);
/* Checking if it is a DVLAN frame */ if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
!(skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) &&
!(skb_cb->flags & ATH12K_SKB_CIPHER_SET) &&
ieee80211_has_protected(hdr->frame_control))
is_dvlan = true;
skip_peer_find:
ret = ath12k_dp_tx(tmp_ar, tmp_arvif,
msdu_copied, true, mcbc_gsn, is_mcast); if (unlikely(ret)) { if (ret == -ENOMEM) { /* Drops are expected during heavy multicast * frame flood. Print with debug log * level to avoid lot of console prints
*/
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "failed to transmit frame %d\n",
ret);
} else {
ath12k_warn(ar->ab, "failed to transmit frame %d\n",
ret);
}
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id,
ar->dp.mac_id + i,
HAL_RXDMA_MONITOR_DST,
DP_RXDMA_REFILL_RING_SIZE,
&tlv_filter); if (ret) {
ath12k_err(ab, "failed to setup filter for monitor buf %d\n",
ret);
}
}
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS,
1, pdev->pdev_id);
if (ret) {
ath12k_err(ab, "failed to enable PMF QOS: %d\n", ret); goto err;
}
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 1,
pdev->pdev_id); if (ret) {
ath12k_err(ab, "failed to enable dynamic bw: %d\n", ret); goto err;
}
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
0, pdev->pdev_id); if (ret) {
ath12k_err(ab, "failed to set ac override for ARP: %d\n",
ret); goto err;
}
ret = ath12k_wmi_send_dfs_phyerr_offload_enable_cmd(ar, pdev->pdev_id); if (ret) {
ath12k_err(ab, "failed to offload radar detection: %d\n",
ret); goto err;
}
ret = ath12k_dp_tx_htt_h2t_ppdu_stats_req(ar,
HTT_PPDU_STATS_TAG_DEFAULT); if (ret) {
ath12k_err(ab, "failed to req ppdu stats: %d\n", ret); goto err;
}
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MESH_MCAST_ENABLE,
1, pdev->pdev_id);
if (ret) {
ath12k_err(ab, "failed to enable MESH MCAST ENABLE: (%d\n", ret); goto err;
}
/* The ar state alone can be turned off for non supported country * without returning the error value. As we need to update the channel * for the next ar.
*/ if (ret) { if (ret == -EINVAL)
ret = 0; goto err;
}
/* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration.
*/
ret = ath12k_mac_config_mon_status_default(ar, true); if (ret && (ret != -EOPNOTSUPP)) {
ath12k_err(ab, "failed to configure monitor status ring with default rx_filter: (%d)\n",
ret); goto err;
}
if (ret == -EOPNOTSUPP)
ath12k_dbg(ab, ATH12K_DBG_MAC, "monitor status config is not yet supported");
/* Configure the hash seed for hash based reo dest ring selection */
ath12k_wmi_pdev_lro_cfg(ar, ar->pdev->pdev_id);
/* allow device to enter IMPS */ if (ab->hw_params->idle_ps) {
ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_IDLE_PS_CONFIG,
1, pdev->pdev_id); if (ret) {
ath12k_err(ab, "failed to enable idle ps: %d\n", ret); goto err;
}
}
switch (ah->state) { case ATH12K_HW_STATE_OFF:
ah->state = ATH12K_HW_STATE_ON; break; case ATH12K_HW_STATE_RESTARTING:
ah->state = ATH12K_HW_STATE_RESTARTED; break; case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: case ATH12K_HW_STATE_ON: case ATH12K_HW_STATE_TM:
ah->state = ATH12K_HW_STATE_OFF;
WARN_ON(1); return -EINVAL;
}
for_each_ar(ah, ar, i) {
ret = ath12k_mac_start(ar); if (ret) {
ah->state = ATH12K_HW_STATE_OFF;
ath12k_err(ar->ab, "fail to start mac operations in pdev idx %d ret %d\n",
ar->pdev_idx, ret); goto fail_start;
}
}
return 0;
fail_start: for (; i > 0; i--) {
ar = ath12k_ah_to_ar(ah, i - 1);
ath12k_mac_stop(ar);
}
return ret;
}
int ath12k_mac_rfkill_config(struct ath12k *ar)
{ struct ath12k_base *ab = ar->ab;
u32 param; int ret;
if (ab->hw_params->rfkill_pin == 0) return -EOPNOTSUPP;
ret = ath12k_mac_config_mon_status_default(ar, false); if (ret && (ret != -EOPNOTSUPP))
ath12k_err(ar->ab, "failed to clear rx_filter for monitor status ring: (%d)\n",
ret);
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in set mbssid params for vif %pM link %u\n",
ahvif->vif->addr, arvif->link_id); return -ENOLINK;
}
tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); if (!tx_arvif) return 0;
if (link_conf->nontransmitted) { if (ath12k_ar_to_hw(ar)->wiphy !=
ath12k_ar_to_hw(tx_arvif->ar)->wiphy) return -EINVAL;
if (ath12k_mac_is_ml_arvif(arvif)) { if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) {
ath12k_warn(ar->ab, "too many MLO links during setting up vdev: %d",
ahvif->vif->valid_links); return -EINVAL;
}
/* In NO_VIRTUAL_MONITOR, its necessary to restrict only one monitor * interface in each radio
*/ if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created) return -EINVAL;
link_id = arvif->link_id;
if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n",
vif->addr, arvif->link_id); return -ENOLINK;
}
}
if (link_conf)
memcpy(arvif->bssid, link_conf->addr, ETH_ALEN); else
memcpy(arvif->bssid, vif->addr, ETH_ALEN);
ret = ath12k_mac_set_key(arvif->ar, key_conf->cmd,
arvif, arsta,
key_conf->key); if (ret)
ath12k_warn(arvif->ar->ab, "unable to apply set key param to vdev %d ret %d\n",
arvif->vdev_id, ret);
free_cache:
list_del(&key_conf->list);
kfree(key_conf);
}
}
if (cache->tx_conf.changed) {
ret = ath12k_mac_conf_tx(arvif, cache->tx_conf.ac,
&cache->tx_conf.tx_queue_params); if (ret)
ath12k_warn(ab, "unable to apply tx config parameters to vdev %d\n",
ret);
}
if (cache->bss_conf_changed) {
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in cache flush for vif %pM link %u\n",
vif->addr, arvif->link_id); return;
}
ath12k_mac_bss_info_changed(ar, arvif, link_conf,
cache->bss_conf_changed);
}
if (!list_empty(&cache->key_conf.list))
ath12k_mac_vif_flush_key_cache(arvif);
if (ah->num_radio == 1)
ar = ah->radio; elseif (ctx)
ar = ath12k_get_ar_by_ctx(hw, ctx); else return NULL;
if (!ar) return NULL;
/* cleanup the scan vdev if we are done scan on that ar * and now we want to create for actual usage.
*/ if (ieee80211_vif_is_mld(vif)) {
scan_link_map = ahvif->links_map & ATH12K_SCAN_LINKS_MASK;
for_each_set_bit(scan_link_id, &scan_link_map, ATH12K_NUM_MAX_LINKS) {
scan_arvif = wiphy_dereference(hw->wiphy,
ahvif->link[scan_link_id]); if (scan_arvif && scan_arvif->ar == ar) {
ar->scan.arvif = NULL;
ath12k_mac_remove_link_interface(hw, scan_arvif);
ath12k_mac_unassign_link_vif(scan_arvif); break;
}
}
}
if (arvif->ar) { /* This is not expected really */ if (WARN_ON(!arvif->is_created)) {
arvif->ar = NULL; return NULL;
}
if (ah->num_radio == 1) return arvif->ar;
/* This can happen as scan vdev gets created during multiple scans * across different radios before a vdev is brought up in * a certain radio.
*/ if (ar != arvif->ar) { if (WARN_ON(arvif->is_started)) return NULL;
/* Assign arvif again here since previous radio switch block * would've unassigned and cleared it.
*/
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); if (vif->type == NL80211_IFTYPE_AP &&
ar->num_peers > (ar->max_num_peers - 1)) {
ath12k_warn(ab, "failed to create vdev due to insufficient peer entry resource in firmware\n"); goto unlock;
}
if (arvif->is_created) goto flush;
if (ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) {
ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
TARGET_NUM_VDEVS(ab)); goto unlock;
}
ret = ath12k_mac_vdev_create(ar, arvif); if (ret) {
ath12k_warn(ab, "failed to create vdev %pM ret %d", vif->addr, ret); goto unlock;
}
flush: /* If the vdev is created during channel assign and not during * add_interface(), Apply any parameters for the vdev which were received * after add_interface, corresponding to this vif.
*/
ath12k_mac_vif_cache_flush(ar, arvif);
unlock: return arvif->ar;
}
/* Allocate Default Queue now and reassign during actual vdev create */
vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++)
vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE;
/* Defer vdev creation until assign_chanctx or hw_scan is initiated as driver * will not know if this interface is an ML vif at this point.
*/ return 0;
}
for (link_id = 0; link_id < ATH12K_NUM_MAX_LINKS; link_id++) { /* if we cached some config but never received assign chanctx, * free the allocated cache.
*/
ath12k_ahvif_put_link_cache(ahvif, link_id);
arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); if (!arvif || !arvif->is_created) continue;
ar = arvif->ar;
/* Scan abortion is in progress since before this, cancel_hw_scan() * is expected to be executed. Since link is anyways going to be removed * now, just cancel the worker and send the scan aborted to user space
*/ if (ar->scan.arvif == arvif) {
wiphy_work_cancel(hw->wiphy, &ar->scan.vdev_clean_wk);
spin_lock_bh(&ar->data_lock);
ar->scan.arvif = NULL; if (!ar->scan.is_roc) { struct cfg80211_scan_info info = {
.aborted = true,
};
staticint ath12k_mac_op_set_antenna(struct ieee80211_hw *hw, int radio_idx,
u32 tx_ant, u32 rx_ant)
{ struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; int ret = 0; int i;
lockdep_assert_wiphy(hw->wiphy);
for_each_ar(ah, ar, i) {
ret = __ath12k_set_antenna(ar, tx_ant, rx_ant); if (ret) break;
}
return ret;
}
staticint ath12k_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params,
u8 link_id)
{ struct ath12k *ar; int ret = -EINVAL;
lockdep_assert_wiphy(hw->wiphy);
ar = ath12k_get_ar_by_vif(hw, vif, link_id); if (!ar) return -EINVAL;
switch (params->action) { case IEEE80211_AMPDU_RX_START:
ret = ath12k_dp_rx_ampdu_start(ar, params, link_id); break; case IEEE80211_AMPDU_RX_STOP:
ret = ath12k_dp_rx_ampdu_stop(ar, params, link_id); break; case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: /* Tx A-MPDU aggregation offloaded to hw/fw so deny mac80211 * Tx aggregation requests.
*/
ret = -EOPNOTSUPP; break;
}
if (ret)
ath12k_warn(ar->ab, "unable to perform ampdu action %d for vif %pM link %u ret %d\n",
params->action, vif->addr, link_id, ret);
spin_lock_bh(&ar->data_lock); /* TODO: In case of there is one more channel context left, populate * rx_channel with the channel of that remaining channel context.
*/
ar->rx_channel = NULL;
spin_unlock_bh(&ar->data_lock);
ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
}
if (hweight16(ahvif->vif->valid_links) > ATH12K_WMI_MLO_MAX_LINKS) return;
ml_arg->enabled = true;
/* Driver always add a new link via VDEV START, FW takes * care of internally adding this link to existing * link vdevs which are advertised as partners below
*/
ml_arg->link_add = true;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ar->ab, "unable to access bss link conf in vdev start for vif %pM link %u\n",
ahvif->vif->addr, arvif->link_id); return -ENOLINK;
}
ret = ath12k_wmi_vdev_start(ar, &arg, restart); if (ret) {
ath12k_warn(ar->ab, "failed to %s WMI vdev %i\n",
restart ? "restart" : "start", arg.vdev_id); return ret;
}
ret = ath12k_mac_vdev_setup_sync(ar); if (ret) {
ath12k_warn(ab, "failed to synchronize setup for vdev %i %s: %d\n",
arg.vdev_id, restart ? "restart" : "start", ret); return ret;
}
/* TODO: For now we only set TPC power here. However when * channel changes, say CSA, it should be updated again.
*/ if (ath12k_mac_supports_tpc(ar, ahvif, chandef)) {
ath12k_mac_fill_reg_tpc_info(ar, arvif, ctx);
ath12k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
&arvif->reg_tpc_info);
}
/* Enable CAC Running Flag in the driver by checking all sub-channel's DFS * state as NL80211_DFS_USABLE which indicates CAC needs to be * done before channel usage. This flag is used to drop rx packets. * during CAC.
*/ /* TODO: Set the flag for other interface types as required */ if (arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP && ctx->radar_enabled &&
cfg80211_chandef_dfs_usable(hw->wiphy, chandef)) {
set_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags);
dfs_cac_time = cfg80211_chandef_dfs_cac_time(hw->wiphy, chandef);
ath12k_dbg(ab, ATH12K_DBG_MAC, "CAC started dfs_cac_time %u center_freq %d center_freq1 %d for vdev %d\n",
dfs_cac_time, arg.freq, arg.band_center_freq1, arg.vdev_id);
}
ret = ath12k_mac_set_txbf_conf(arvif); if (ret)
ath12k_warn(ab, "failed to set txbf conf for vdev %d: %d\n",
arvif->vdev_id, ret);
/* Firmware expect vdev_restart only if vdev is up. * If vdev is down then it expect vdev_stop->vdev_start.
*/ if (arvif->is_up) {
ret = ath12k_mac_vdev_restart(arvif, vifs[i].new_ctx); if (ret) {
ath12k_warn(ab, "failed to restart vdev %d: %d\n",
arvif->vdev_id, ret); continue;
}
} else {
ret = ath12k_mac_vdev_stop(arvif); if (ret) {
ath12k_warn(ab, "failed to stop vdev %d: %d\n",
arvif->vdev_id, ret); continue;
}
ret = ath12k_mac_vdev_start(arvif, vifs[i].new_ctx); if (ret)
ath12k_warn(ab, "failed to start vdev %d: %d\n",
arvif->vdev_id, ret); continue;
}
ret = ath12k_mac_setup_bcn_tmpl(arvif); if (ret)
ath12k_warn(ab, "failed to update bcn tmpl during csa: %d\n",
ret);
memset(¶ms, 0, sizeof(params));
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 = link_conf->bssid_index;
params.nontx_profile_cnt = 1 << link_conf->bssid_indicator;
}
ret = ath12k_wmi_vdev_up(arvif->ar, ¶ms); if (ret) {
ath12k_warn(ab, "failed to bring vdev up %d: %d\n",
arvif->vdev_id, ret); continue;
}
ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif,
vifs[i].new_ctx->def); if (ret) {
ath12k_warn(ar->ab, "failed to update puncturing bitmap %02x and width %d: %d\n",
vifs[i].new_ctx->def.punctured,
vifs[i].new_ctx->def.width, ret); continue;
}
}
/* Restart the internal monitor vdev on new channel */ if (!monitor_vif && ar->monitor_vdev_created) { if (!ath12k_mac_monitor_stop(ar))
ath12k_mac_monitor_start(ar);
}
}
/* This shouldn't really happen because channel switching should use * switch_vif_chanctx().
*/ if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) return;
link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) {
ath12k_warn(ab, "failed to get link conf for vdev %u\n", arvif->vdev_id); return -EINVAL;
}
chanctx = wiphy_dereference(ath12k_ar_to_hw(arvif->ar)->wiphy,
link_conf->chanctx_conf);
ret = ath12k_mac_vdev_start(arvif, chanctx); if (ret) {
ath12k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
arvif->vdev_id, vif->addr,
chanctx->def.chan->center_freq, ret); return ret;
}
if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
ret = ath12k_monitor_vdev_up(ar, arvif->vdev_id); if (ret) {
ath12k_warn(ab, "failed put monitor up: %d\n", ret); return ret;
}
}
/* It is to get the lowest channel number's center frequency of the chan. * For example, * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1 * with center frequency 5955, its diff is 5965 - 5955 = 10. * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1 * with center frequency 5955, its diff is 5985 - 5955 = 30. * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1 * with center frequency 5955, its diff is 6025 - 5955 = 70. * bandwidth=320 MHz, center frequency is 6105, lowest channel is 1 * with center frequency 5955, its diff is 6105 - 5955 = 70.
*/ switch (chan_def->width) { case NL80211_CHAN_WIDTH_320:
diff_seq = 150; break; case NL80211_CHAN_WIDTH_160:
diff_seq = 70; break; case NL80211_CHAN_WIDTH_80:
diff_seq = 30; break; case NL80211_CHAN_WIDTH_40:
diff_seq = 10; break; default:
diff_seq = 0;
}
/* It is to get the center frequency of the specific bandwidth. * start_seq means the lowest channel number's center frequency. * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz. * For example, * lowest channel is 1, its center frequency 5955, * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0. * lowest channel is 1, its center frequency 5955, * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10. * lowest channel is 1, its center frequency 5955, * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30. * lowest channel is 1, its center frequency 5955, * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
*/
seg_seq = 10 * (BIT(seq) - 1); return seg_seq + start_seq;
}
staticvoid ath12k_mac_get_psd_channel(struct ath12k *ar,
u16 step_freq,
u16 *start_freq,
u16 *center_freq,
u8 i, struct ieee80211_channel **temp_chan,
s8 *tx_power)
{ /* It is to get the center frequency for each 20 MHz. * For example, if the chan is 160 MHz and center frequency is 6025, * then it include 8 channels, they are 1/5/9/13/17/21/25/29, * channel number 1's center frequency is 5955, it is parameter start_freq. * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels. * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7, * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095, * the gap is 20 for each channel, parameter step_freq means the gap. * after get the center frequency of each channel, it is easy to find the * struct ieee80211_channel of it and get the max_reg_power.
*/
*center_freq = *start_freq + i * step_freq;
*temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq);
*tx_power = (*temp_chan)->max_reg_power;
}
staticvoid ath12k_mac_get_eirp_power(struct ath12k *ar,
u16 *start_freq,
u16 *center_freq,
u8 i, struct ieee80211_channel **temp_chan, struct cfg80211_chan_def *def,
s8 *tx_power)
{ /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/ * 160 MHz bandwidth, and then plus 10 to the center frequency, * it is the center frequency of a channel number. * For example, when configured channel number is 1. * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975, * then it is channel number 5. * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995, * then it is channel number 9. * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035, * then it is channel number 17. * after get the center frequency of each channel, it is easy to find the * struct ieee80211_channel of it and get the max_reg_power.
*/
*center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i);
/* For the 20 MHz, its center frequency is same with same channel */ if (i != 0)
*center_freq += 10;
for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { /* STA received TPE IE*/ if (is_tpe_present) { /* local power is PSD power*/ if (chan->flags & IEEE80211_CHAN_PSD) { /* Connecting AP is psd power */ if (reg_tpc_info->is_psd_power) {
is_psd_power = true;
ath12k_mac_get_psd_channel(ar, 20,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&tx_power);
psd_power = temp_chan->psd;
eirp_power = tx_power;
max_tx_power[pwr_lvl_idx] =
min_t(s8,
psd_power,
reg_tpc_info->tpe[pwr_lvl_idx]); /* Connecting AP is not psd power */
} else {
ath12k_mac_get_eirp_power(ar,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&ctx->def,
&tx_power);
psd_power = temp_chan->psd; /* convert psd power to EIRP power based * on channel width
*/
tx_power =
min_t(s8, tx_power,
psd_power + 13 + pwr_lvl_idx * 3);
max_tx_power[pwr_lvl_idx] =
min_t(s8,
tx_power,
reg_tpc_info->tpe[pwr_lvl_idx]);
} /* local power is not PSD power */
} else { /* Connecting AP is psd power */ if (reg_tpc_info->is_psd_power) {
is_psd_power = true;
ath12k_mac_get_psd_channel(ar, 20,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&tx_power);
eirp_power = tx_power;
max_tx_power[pwr_lvl_idx] =
reg_tpc_info->tpe[pwr_lvl_idx]; /* Connecting AP is not psd power */
} else {
ath12k_mac_get_eirp_power(ar,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&ctx->def,
&tx_power);
max_tx_power[pwr_lvl_idx] =
min_t(s8,
tx_power,
reg_tpc_info->tpe[pwr_lvl_idx]);
}
} /* STA not received TPE IE */
} else { /* local power is PSD power*/ if (chan->flags & IEEE80211_CHAN_PSD) {
is_psd_power = true;
ath12k_mac_get_psd_channel(ar, 20,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&tx_power);
psd_power = temp_chan->psd;
eirp_power = tx_power;
max_tx_power[pwr_lvl_idx] = psd_power;
} else {
ath12k_mac_get_eirp_power(ar,
&start_freq,
¢er_freq,
pwr_lvl_idx,
&temp_chan,
&ctx->def,
&tx_power);
max_tx_power[pwr_lvl_idx] = tx_power;
}
}
if (is_psd_power) { /* If AP local power constraint is present */ if (pwr_reduction)
eirp_power = eirp_power - pwr_reduction;
/* If firmware updated max tx power is non zero, then take * the min of firmware updated ap tx power * and max power derived from above mentioned parameters.
*/
ath12k_dbg(ab, ATH12K_DBG_MAC, "eirp power : %d firmware report power : %d\n",
eirp_power, ar->max_allowed_tx_power); /* Firmware reports lower max_allowed_tx_power during vdev * start response. In case of 6 GHz, firmware is not aware * of EIRP power unless driver sets EIRP power through WMI * TPC command. So radio which does not support idle power * save can set maximum calculated EIRP power directly to * firmware through TPC command without min comparison with * vdev start response's max_allowed_tx_power.
*/ if (ar->max_allowed_tx_power && ab->hw_params->idle_ps)
eirp_power = min_t(s8,
eirp_power,
ar->max_allowed_tx_power);
} else { /* If AP local power constraint is present */ if (pwr_reduction)
max_tx_power[pwr_lvl_idx] =
max_tx_power[pwr_lvl_idx] - pwr_reduction; /* If firmware updated max tx power is non zero, then take * the min of firmware updated ap tx power * and max power derived from above mentioned parameters.
*/ if (ar->max_allowed_tx_power && ab->hw_params->idle_ps)
max_tx_power[pwr_lvl_idx] =
min_t(s8,
max_tx_power[pwr_lvl_idx],
ar->max_allowed_tx_power);
}
reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
max_tx_power[pwr_lvl_idx];
}
reg_tpc_info->num_pwr_levels = num_pwr_levels;
reg_tpc_info->is_psd_power = is_psd_power;
reg_tpc_info->eirp_power = eirp_power; if (ahvif->vdev_type == WMI_VDEV_TYPE_STA)
reg_6ghz_power_mode = bss_conf->power_type; else /* For now, LPI is the only supported AP power mode */
reg_6ghz_power_mode = IEEE80211_REG_LPI_AP;
/* For multi radio wiphy, the vdev was not created during add_interface * create now since we have a channel ctx now to assign to a specific ar/fw
*/
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); if (!arvif) {
WARN_ON(1); return -ENOMEM;
}
ar = ath12k_mac_assign_vif_to_vdev(hw, arvif, ctx); if (!ar) {
ath12k_hw_warn(ah, "failed to assign chanctx for vif %pM link id %u link vif is already started",
vif->addr, link_id); return -EINVAL;
}
/* for some targets bss peer must be created before vdev_start */ if (ab->hw_params->vdev_start_delay &&
ahvif->vdev_type != WMI_VDEV_TYPE_AP &&
ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
!ath12k_peer_exist_by_vdev_id(ab, arvif->vdev_id)) {
ret = 0; goto out;
}
if (WARN_ON(arvif->is_started)) {
ret = -EBUSY; goto out;
}
if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
ret = ath12k_mac_monitor_start(ar); if (ret) {
ath12k_mac_monitor_vdev_delete(ar); goto out;
}
arvif->is_started = true; goto out;
}
ret = ath12k_mac_vdev_start(arvif, ctx); if (ret) {
ath12k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
arvif->vdev_id, vif->addr,
ctx->def.chan->center_freq, ret); goto out;
}
/* The vif is expected to be attached to an ar's VDEV. * We leave the vif/vdev in this function as is * and not delete the vdev symmetric to assign_vif_chanctx() * the VDEV will be deleted and unassigned either during * remove_interface() or when there is a change in channel * that moves the vif to a new ar
*/ if (!arvif || !arvif->is_created) return;
staticint
ath12k_set_vdev_param_to_all_vifs(struct ath12k *ar, int param, u32 value)
{ struct ath12k_link_vif *arvif; int ret = 0;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
list_for_each_entry(arvif, &ar->arvifs, list) {
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "setting mac vdev %d param %d value %d\n",
param, arvif->vdev_id, value);
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param, value); if (ret) {
ath12k_warn(ar->ab, "failed to set param %d for vdev %d: %d\n",
param, arvif->vdev_id, ret); break;
}
}
return ret;
}
/* mac80211 stores device specific RTS/Fragmentation threshold value, * this is set interface specific to firmware from ath12k driver
*/ staticint ath12k_mac_op_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value)
{ struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar; int param_id = WMI_VDEV_PARAM_RTS_THRESHOLD, ret = 0, i;
lockdep_assert_wiphy(hw->wiphy);
/* Currently we set the rts threshold value to all the vifs across * all radios of the single wiphy. * TODO Once support for vif specific RTS threshold in mac80211 is * available, ath12k can make use of it.
*/
for_each_ar(ah, ar, i) {
ret = ath12k_set_vdev_param_to_all_vifs(ar, param_id, value); if (ret) {
ath12k_warn(ar->ab, "failed to set RTS config for all vdevs of pdev %d",
ar->pdev->pdev_id); break;
}
}
return ret;
}
staticint ath12k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value)
{ /* Even though there's a WMI vdev param for fragmentation threshold no * known firmware actually implements it. Moreover it is not possible to * rely frame fragmentation to mac80211 because firmware clears the * "more fragments" bit in frame control making it impossible for remote * devices to reassemble frames. * * Hence implement a dummy callback just to say fragmentation isn't * supported. This effectively prevents mac80211 from doing frame * fragmentation in software.
*/
lockdep_assert_wiphy(hw->wiphy);
return -EOPNOTSUPP;
}
staticint ath12k_mac_flush(struct ath12k *ar)
{ long time_left; int ret = 0;
time_left = wait_event_timeout(ar->dp.tx_empty_waitq,
(atomic_read(&ar->dp.num_tx_pending) == 0),
ATH12K_FLUSH_TIMEOUT); if (time_left == 0) {
ath12k_warn(ar->ab, "failed to flush transmit queue, data pkts pending %d\n",
atomic_read(&ar->dp.num_tx_pending));
ret = -ETIMEDOUT;
}
time_left = wait_event_timeout(ar->txmgmt_empty_waitq,
(atomic_read(&ar->num_pending_mgmt_tx) == 0),
ATH12K_FLUSH_TIMEOUT); if (time_left == 0) {
ath12k_warn(ar->ab, "failed to flush mgmt transmit queue, mgmt pkts pending %d\n",
atomic_read(&ar->num_pending_mgmt_tx));
ret = -ETIMEDOUT;
}
return ret;
}
int ath12k_mac_wait_tx_complete(struct ath12k *ar)
{
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_SGI, he_gi); if (ret) {
ath12k_warn(ar->ab, "failed to set HE GI:%d, error:%d\n",
he_gi, ret); return ret;
} /* start from 1 */ if (he_ltf != 0xFF)
he_ltf += 1;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_HE_LTF, he_ltf); if (ret) {
ath12k_warn(ar->ab, "failed to set HE LTF:%d, error:%d\n",
he_ltf, ret); return ret;
} return 0;
}
if (he_gi != 0xFF) { switch (he_gi) { case NL80211_RATE_INFO_HE_GI_0_8:
he_gi = WMI_AUTORATE_800NS_GI; break; case NL80211_RATE_INFO_HE_GI_1_6:
he_gi = WMI_AUTORATE_1600NS_GI; break; case NL80211_RATE_INFO_HE_GI_3_2:
he_gi = WMI_AUTORATE_3200NS_GI; break; default:
ath12k_warn(ar->ab, "Invalid GI\n"); return -EINVAL;
}
}
if (he_ltf != 0xFF) { switch (he_ltf) { case NL80211_RATE_INFO_HE_1XLTF:
he_ltf = WMI_HE_AUTORATE_LTF_1X; break; case NL80211_RATE_INFO_HE_2XLTF:
he_ltf = WMI_HE_AUTORATE_LTF_2X; break; case NL80211_RATE_INFO_HE_4XLTF:
he_ltf = WMI_HE_AUTORATE_LTF_4X; break; default:
ath12k_warn(ar->ab, "Invalid LTF\n"); return -EINVAL;
}
}
he_ar_gi_ltf = he_gi | he_ltf;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_AUTORATE_MISC_CFG,
he_ar_gi_ltf); if (ret) {
ath12k_warn(ar->ab, "failed to set HE autorate GI:%u, LTF:%u params, error:%d\n",
he_gi, he_ltf, ret); return ret;
}
ret = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id,
WMI_PEER_PARAM_FIXED_RATE,
WMI_FIXED_RATE_NONE); if (ret)
ath12k_warn(ar->ab, "failed to disable peer fixed rate for STA %pM ret %d\n",
arsta->addr, ret);
}
/* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it * requires passing at least one of used basic rates along with them. * Fixed rate setting across different preambles(legacy, HT, VHT) is * not supported by the FW. Hence use of FIXED_RATE vdev param is not * suitable for setting single HT/VHT rates. * But, there could be a single basic rate passed from userspace which * can be done through the FIXED_RATE param.
*/ if (ath12k_mac_has_single_legacy_rate(ar, band, mask)) {
ret = ath12k_mac_get_single_legacy_rate(ar, band, mask, &rate,
&nss); if (ret) {
ath12k_warn(ar->ab, "failed to get single legacy rate for vdev %i: %d\n",
arvif->vdev_id, ret); goto out;
}
if (!ath12k_mac_validate_fixed_rate_settings(ar, band,
mask, arvif->link_id))
ath12k_warn(ar->ab, "failed to update fixed rate settings due to mcs/nss incompatibility\n");
/* If multiple rates across different preambles are given * we can reconfigure this info with all peers using PEER_ASSOC * command with the below exception cases. * - Single VHT Rate : peer_assoc command accommodates only MCS * range values i.e 0-7, 0-8, 0-9 for VHT. Though mac80211 * mandates passing basic rates along with HT/VHT rates, FW * doesn't allow switching from VHT to Legacy. Hence instead of * setting legacy and VHT rates using RATEMASK_CMD vdev cmd, * we could set this VHT rate as peer fixed rate param, which * will override FIXED rate and FW rate control algorithm. * If single VHT rate is passed along with HT rates, we select * the VHT rate as fixed rate for vht peers. * - Multiple VHT Rates : When Multiple VHT rates are given,this * can be set using RATEMASK CMD which uses FW rate-ctl alg. * TODO: Setting multiple VHT MCS and replacing peer_assoc with * RATEMASK_CMDID can cover all use cases of setting rates * across multiple preambles and rates within same type. * But requires more validation of the command at this point.
*/
if (!ath12k_mac_vht_mcs_range_present(ar, band, mask) &&
num_rates > 1) { /* TODO: Handle multiple VHT MCS values setting using * RATEMASK CMD
*/
ath12k_warn(ar->ab, "Setting more than one MCS Value in bitrate mask not supported\n");
ret = -EINVAL; goto out;
}
if (!ath12k_mac_he_mcs_range_present(ar, band, mask) &&
num_rates > 1) {
ath12k_warn(ar->ab, "Setting more than one HE MCS Value in bitrate mask not supported\n");
ret = -EINVAL; goto out;
}
ieee80211_iterate_stations_mtx(hw,
ath12k_mac_disable_peer_fixed_rate,
arvif);
/* When there are multiple radios in an SOC, * the recovery has to be done for each radio
*/ if (recovery_count == ab->num_radios) {
atomic_dec(&ab->reset_count);
complete(&ab->reset_complete);
ab->is_reset = false;
atomic_set(&ab->fail_cont_count, 0);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset success\n");
}
}
list_for_each_entry(arvif, &ar->arvifs, list) {
ahvif = arvif->ahvif;
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reconfig cipher %d up %d vdev type %d\n",
ahvif->key_cipher,
arvif->is_up,
ahvif->vdev_type);
/* After trigger disconnect, then upper layer will * trigger connect again, then the PN number of * upper layer will be reset to keep up with AP * side, hence PN number mismatch will not happen.
*/ if (arvif->is_up &&
ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) {
ieee80211_hw_restart_disconnect(ahvif->vif);
ar = ath12k_mac_select_scan_device(hw, vif, chan->center_freq); if (!ar) return -EINVAL;
/* check if any of the links of ML VIF is already started on * radio(ar) corresponding to given scan frequency and use it, * if not use deflink(link 0) for scan purpose.
*/
link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar);
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); /* If the vif is already assigned to a specific vdev of an ar, * check whether its already started, vdev which is started * are not allowed to switch to a new radio. * If the vdev is not started, but was earlier created on a * different ar, delete that vdev and create a new one. We don't * delete at the scan stop as an optimization to avoid redundant * delete-create vdev's for the same ar, in case the request is * always on the same band for the vif
*/ if (arvif->is_created) { if (WARN_ON(!arvif->ar)) return -EINVAL;
if (ar != arvif->ar && arvif->is_started) return -EBUSY;
if (ar != arvif->ar) {
ath12k_mac_remove_link_interface(hw, arvif);
ath12k_mac_unassign_link_vif(arvif);
} else {
create = false;
}
}
if (create) {
arvif = ath12k_mac_assign_link_vif(ah, vif, link_id);
ret = ath12k_mac_vdev_create(ar, arvif); if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev for roc: %d\n",
ret); return ret;
}
}
spin_lock_bh(&ar->data_lock);
switch (ar->scan.state) { case ATH12K_SCAN_IDLE:
reinit_completion(&ar->scan.started);
reinit_completion(&ar->scan.completed);
reinit_completion(&ar->scan.on_channel);
ar->scan.state = ATH12K_SCAN_STARTING;
ar->scan.is_roc = true;
ar->scan.arvif = arvif;
ar->scan.roc_freq = chan->center_freq;
ar->scan.roc_notify = true;
ret = 0; break; case ATH12K_SCAN_STARTING: case ATH12K_SCAN_RUNNING: case ATH12K_SCAN_ABORTING:
ret = -EBUSY; break;
}
ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); if (ret == 0) {
ath12k_warn(ar->ab, "failed to switch to channel for roc scan\n");
ret = ath12k_scan_stop(ar); if (ret)
ath12k_warn(ar->ab, "failed to stop scan: %d\n", ret); return -ETIMEDOUT;
}
if (orig_band->band != new_band->band) return -EINVAL;
for (i = 0; i < new_band->n_channels; i++) { if (new_band->channels[i].flags & IEEE80211_CHAN_DISABLED) continue; /* An enabled channel in new_band should not be already enabled * in the orig_band
*/ if (WARN_ON(!(orig_band->channels[i].flags &
IEEE80211_CHAN_DISABLED))) return -EINVAL;
orig_band->channels[i].flags &= ~IEEE80211_CHAN_DISABLED;
} return 0;
}
if (!bands[NL80211_BAND_2GHZ]) {
bands[NL80211_BAND_2GHZ] = band;
} else { /* Split mac in same band under same wiphy */
ret = ath12k_mac_update_band(ar, bands[NL80211_BAND_2GHZ], band); if (ret) {
kfree(channels);
band->channels = NULL; return ret;
}
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac pdev %u identified as 2 GHz split mac with start freq %d end freq %d",
ar->pdev->pdev_id,
KHZ_TO_MHZ(ar->freq_range.start_freq),
KHZ_TO_MHZ(ar->freq_range.end_freq));
}
}
if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6GHZ_FREQ) {
channels = kmemdup(ath12k_6ghz_channels, sizeof(ath12k_6ghz_channels), GFP_KERNEL); if (!channels) {
kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); return -ENOMEM;
}
if (!bands[NL80211_BAND_6GHZ]) {
bands[NL80211_BAND_6GHZ] = band;
} else { /* Split mac in same band under same wiphy */
ret = ath12k_mac_update_band(ar,
bands[NL80211_BAND_6GHZ],
band); if (ret) {
kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
ar->mac.sbands[NL80211_BAND_2GHZ].channels = NULL;
kfree(channels);
band->channels = NULL; return ret;
}
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac pdev %u identified as 6 GHz split mac with start freq %d end freq %d",
ar->pdev->pdev_id,
KHZ_TO_MHZ(ar->freq_range.start_freq),
KHZ_TO_MHZ(ar->freq_range.end_freq));
}
}
if (reg_cap->low_5ghz_chan < ATH12K_MIN_6GHZ_FREQ) {
channels = kmemdup(ath12k_5ghz_channels, sizeof(ath12k_5ghz_channels),
GFP_KERNEL); if (!channels) {
kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); return -ENOMEM;
}
staticint ath12k_mac_setup_iface_combinations(struct ath12k_hw *ah)
{ struct ieee80211_iface_combination *combinations, *comb; struct wiphy *wiphy = ah->hw->wiphy; struct wiphy_radio *radio; int n_combinations = 1; struct ath12k *ar; int i, ret;
if (ah->num_radio == 1) {
ar = &ah->radio[0];
if (ar->ab->hw_params->single_pdev_only)
n_combinations = 2;
combinations = kcalloc(n_combinations, sizeof(*combinations),
GFP_KERNEL); if (!combinations) return -ENOMEM;
ret = ath12k_mac_setup_radio_iface_comb(ar, combinations); if (ret) {
ath12k_hw_warn(ah, "failed to setup radio interface combinations for one radio: %d",
ret); goto err_free_combinations;
}
combinations = kcalloc(n_combinations, sizeof(*combinations), GFP_KERNEL); if (!combinations) return -ENOMEM;
/* there are multiple radios */
radio = kcalloc(ah->num_radio, sizeof(*radio), GFP_KERNEL); if (!radio) {
ret = -ENOMEM; goto err_free_combinations;
}
for_each_ar(ah, ar, i) {
comb = kzalloc(sizeof(*comb), GFP_KERNEL); if (!comb) {
ret = -ENOMEM; goto err_free_radios;
}
ret = ath12k_mac_setup_radio_iface_comb(ar, comb); if (ret) {
ath12k_hw_warn(ah, "failed to setup radio interface combinations for radio %d: %d",
i, ret);
kfree(comb); goto err_free_radios;
}
ret = ath12k_mac_setup_register(ar, &ht_cap_info, hw->wiphy->bands); if (ret) goto err_cleanup_unregister;
/* 6 GHz does not support HT Cap, hence do not consider it */ if (!ar->supports_6ghz)
ht_cap &= ht_cap_info;
wiphy->max_ap_assoc_sta += ar->max_num_stations;
/* Advertise the max antenna support of all radios, driver can handle * per pdev specific antenna setting based on pdev cap when antenna * changes are made
*/
cap = &pdev->cap;
/* TODO: Check if HT capability advertised from firmware is different * for each band for a dual band capable radio. It will be tricky to * handle it when the ht capability different for each band.
*/ if (ht_cap & WMI_HT_CAP_DYNAMIC_SMPS ||
(is_6ghz && ab->hw_params->supports_dynamic_smps_6ghz))
wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS;
/* MLO is not yet supported so disable Wireless Extensions for now * to make sure ath12k users don't use it. This flag can be removed * once WIPHY_FLAG_SUPPORTS_MLO is enabled.
*/
wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT;
/* Copy over MLO related capabilities received from * WMI_SERVICE_READY_EXT2_EVENT if single_chip_mlo_supp is set.
*/ if (ab->ag->mlo_capable) {
ath12k_iftypes_ext_capa[2].eml_capabilities = cap->eml_cap;
ath12k_iftypes_ext_capa[2].mld_capa_and_ops = cap->mld_cap;
wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO;
ret = ath12k_wow_init(ar); if (ret) {
ath12k_warn(ar->ab, "failed to init wow: %d\n", ret); goto err_cleanup_if_combs;
}
/* Boot-time regulatory updates have already been processed. * Mark them as complete now, because after registration, * cfg80211 will notify us again if there are any pending hints. * We need to wait for those hints to be processed, so it's * important to mark the boot-time updates as complete before * proceeding with registration.
*/
for_each_ar(ah, ar, i)
complete_all(&ar->regd_update_completed);
ret = ieee80211_register_hw(hw); if (ret) {
ath12k_err(ab, "ieee80211 registration failed: %d\n", ret); goto err_cleanup_if_combs;
}
if (is_monitor_disable) /* There's a race between calling ieee80211_register_hw() * and here where the monitor mode is enabled for a little * while. But that time is so short and in practise it make * a difference in real life.
*/
wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR);
for_each_ar(ah, ar, i) { /* Apply the regd received during initialization */
ret = ath12k_regd_update(ar, true); if (ret) {
ath12k_err(ar->ab, "ath12k regd update failed: %d\n", ret); goto err_unregister_hw;
}
if (ar->ab->hw_params->current_cc_support && ab->new_alpha2[0]) { struct wmi_set_current_country_arg current_cc = {};
ar->wmi = &ab->wmi_ab.wmi[pdev_idx]; /* FIXME: wmi[0] is already initialized during attach, * Should we do this again?
*/
ath12k_wmi_pdev_attach(ab, pdev_idx);
if (WARN_ON(total_radio > ATH12K_GROUP_MAX_RADIO)) return -ENOSPC;
/* All pdev get combined and register as single wiphy based on * hardware group which participate in multi-link operation else * each pdev get register separately.
*/ if (ag->mlo_capable)
radio_per_hw = total_radio; else
radio_per_hw = 1;
num_hw = total_radio / radio_per_hw;
ag->num_hw = 0;
device_id = 0;
mac_id = 0; for (i = 0; i < num_hw; i++) { for (j = 0; j < radio_per_hw; j++) { if (device_id >= ag->num_devices || !ag->ab[device_id]) {
ret = -ENOSPC; goto err;
}
ab = ag->ab[device_id];
pdev_map[j].ab = ab;
pdev_map[j].pdev_idx = mac_id;
mac_id++;
/* If mac_id falls beyond the current device MACs then * move to next device
*/ if (mac_id >= ab->num_radios) {
mac_id = 0;
device_id++;
}
}
ab = pdev_map->ab;
ah = ath12k_mac_hw_allocate(ag, pdev_map, radio_per_hw); if (!ah) {
ath12k_warn(ab, "failed to allocate mac80211 hw device for hw_idx %d\n",
i);
ret = -ENOMEM; goto err;
}
ah->dev = ab->dev;
ag->ah[i] = ah;
ag->num_hw++;
}
return 0;
err: for (i = i - 1; i >= 0; i--) {
ah = ath12k_ag_to_ah(ag, i); if (!ah) continue;
ath12k_mac_hw_destroy(ah);
ath12k_ag_set_ah(ag, i, NULL);
}
ret = ath12k_wmi_sta_keepalive(ar, &arg); if (ret) {
ath12k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
return 0;
}
Messung V0.5 in Prozent
¤ 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.0.498Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
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.